AxonFramework在聚合中處理命令

建議在包含處理狀態命令的聚合中直接定義命令處理器,因爲命令處理器有可能需要該集合的狀態來執行其任務。

要在一個聚合上定義一個命令處理器,只需用@CommandHandler註解命令處理方法即可。帶@CommandHandler註解方法的規則和其他處理方法都是一樣的。然而,命令不僅通過他們的有效載荷(payload)進行路由。命令消息攜帶一個名字,該名稱默認爲命令對象的完全限定類名。

默認情況下,帶@CommandHandler註解的方法允許以下參數類型:

  • 第一個參數是命令消息的有效載荷。它的類型也可能是Message或CommandMessage,如果@CommandHandler 註解明確定義命令處理器的名稱。默認情況下,命令名是命令的有效載荷的完全限定類名。
  • 用@MetaDataValue註解的參數,將用註解上的鍵對元數據值進行解析。如果需要爲false(默認值),則在元數據值不存在時傳遞NULL。如果需要爲True,在元數據值不存在時,該解析器將不匹配並阻止該方法被調用。
  • 參數的類型元數據將注入整個CommandMessage的元數據。
  • UnitOfWork類型的參數獲取當前工作單元注入。這允許命令處理器註冊的行爲在工作單元的特定階段執行,或獲得與它註冊的資源的訪問。
  • Message或CommandMessage類型的參數,將得到完整的消息,包括有效載荷和元數據。如果方法需要多個元數據字段或包裝消息的其他屬性,則此方法非常有用。

爲了使Axon知道哪一個聚合類型的實例應該處理命令消息,命令對象的屬性傳送聚合標識符,必須用@TargetAggregateIdentifier註解。註解可以放置在任何字段或訪問器方法上(例如getter)。

注意
當@CommandHandler註解放在一個聚合的構造函數上時,相應的命令將創建一個新的聚合實例,並將它添加到存儲庫。這些命令不需要針對特的定聚合實例。因此,這些命令不需要任何@TargetAggregateIdentifier或@TargetAggregateVersion註解,也不會調用自定義CommandTargetResolver。
當一個命令創建一個聚合實例時,該命令的回調函數在命令執行成功執行後,將得到聚合標識符。

public class MyAggregate {
    @AggregateIdentifier
    private String id;
    @CommandHandler
    public MyAggregate(CreateMyAggregateCommand command) {
        apply(new MyAggregateCreatedEvent(IdentifierFactory.getInstance().generateIden
tifier()));
    }
    // no-arg constructor for Axon
    MyAggregate() {
    }
    @CommandHandler
    public void doSomething(DoSomethingCommand command) {
    // do something...
    }
    // code omitted for brevity. The event handler for MyAggregateCreatedEvent must set the id field
}
public class DoSomethingCommand {
    @TargetAggregateIdentifier
    private String aggregateId;
    // code omitted for brevity
}

Axon的配置API可用於配置聚合。例如:

Configurer configurer = ...
// to use defaults:
configurer.configureAggreate(MyAggregate.class);
// allowing customizations:
configurer.configureAggregate(
AggregateConfigurer.defaultConfiguration(MyAggregate.class)
.configureCommandTargetResolver(c -> new CustomCommandTargetResolver()));

@CommandHandler註釋並不侷限於聚合根。把所有命令處理器放在根裏,有時會導致聚合根中存在大量的方法,而它們中的許多隻簡單地調用轉發給底層實體之一。如果是這樣,你可以把@CommandHandler註解在一個底層的實體的方法上。Axon找到這些帶註釋的方法,聚合根中聲明的實體字段必須用@AggregateMember標明。注意,命令處理器只檢查帶註解的字段的聲明類型。如果一個字段值爲空時傳入命令到實體,就會拋出一個異常。

public class MyAggregate {
    @AggregateIdentifier
    private String id;
    @AggregateMember
    private MyEntity entity;
    @CommandHandler
    public MyAggregate(CreateMyAggregateCommand command) {
        apply(new MyAggregateCreatedEvent(...);
    }
    // no-arg constructor for Axon
    MyAggregate() {
    }
    @CommandHandler
    public void doSomething(DoSomethingCommand command) {
        // do something...
    }
    // code omitted for brevity. The event handler for MyAggregateCreatedEvent must set the id field
    // and somewhere in the lifecycle, a value for "entity" must be assigned to be able to accept
    // DoSomethingInEntityCommand commands.
}
public class MyEntity {
    @CommandHandler
    public void handleSomeCommand(DoSomethingInEntityCommand command) {
        // do something
    }
}

請注意,在聚合中每個命令必須只對應一個處理器。這意味着你不能用@CommandHandler標註多個實體(either root nor not,包含是根和不是根的所有實體)來處理相同的命令類型。如果你需要有條件地路由命令到一個實體,這些實體的父類應該處理命令,並根據apply的條件轉發該命令。

字段的運行時類型不需要精確地聲明類型。然而,@CommandHandle方法只檢查被@AggregateMember標記的字段的聲明類型。

也可以用@AggregateMember去註釋包含實體的集合和Map。在後一種情況下,map的值有望包含實體,而鍵包含一個用作它們引用的值。

作爲一個命令需要被路由到正確的實例,這些實例必須被正確地標識。它們的“ID”字段必須用@ EntityId標記。命令的屬性將用於查找該消息應被路由到的實體,默認爲被標識的字段的名稱。例如,當標記一個名爲“myentityid”字段,命令必須具有相同名稱的屬性。這意味着必須提供個getmyentityid或myentityid()方法。如果字段的名稱和路由屬性不同,你可以提供一個值顯式使用 @EntityId(routingKey = “customRoutingProperty”)。

如果在帶註解的集合和Map中沒有實體能被找到,Axon會拋出一個IllegalStateException異常。顯然,聚合不能夠在那個時間點上處理命令。

注意
字段聲明的集合或Map應該包含適當的泛型,允許Axon識別實體的類型包含在集合或Map中。如果不可能添加泛型在聲明中(例如因爲你已經使用了一個自定義泛型類型的實現),你必須指定實體的類型,用於entityType屬性@AggregateMember註解。

外部命令處理器

在某些情況下,想要直接向一個聚合實例路由命令是不可能。在這種情況下,可以註冊一個命令處理器對象。命令處理器對象是一個簡單的(常規的)對象,是帶@CommandHandle註解的方法。與集合的情況不同,命令處理器對象只有單個實例,該對象處理其方法中聲明的所有命令類型。

public class MyAnnotatedHandler {
@CommandHandler
public void handleSomeCommand(SomeCommand command, @MetaDataValue("userId") String
userId) {
// whatever logic here
}
@CommandHandler(commandName = "myCustomCommand")
public void handleCustomCommand(SomeCommand command) {
// handling logic here
}
}
// To register the annotated handlers to the command bus:
Configurer configurer = ...
configurer.registerCommandHandler(c -> new MyAnnotatedHandler());

作者:勇赴
鏈接:https://www.jianshu.com/p/64ddd5a8f517
來源:簡書
著作權歸作者所有。商業轉載請聯繫作者獲得授權,非商業轉載請註明出處。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章