1

最近CQRSを勉強しているので、axon-framework(Java CRQSフレームワーク)でサンプルプロジェクトを始めました。

クイックスタートによると、以下のようになりました。

public class CreditEntryUnitTests {

    private FixtureConfiguration fixture;

    @Before
    public void setUp() throws Exception {
        fixture = Fixtures.newGivenWhenThenFixture(CreditEntry.class);
    }

    @Test
    public void creditEntryCreated() throws Throwable {
        final Long entryId = 1L;
        final int amount = 100;

        fixture.given().when(new CreateCreditEntryCommand(entryId, amount))
            .expectEvents(new CreditEntryCreatedEvent(entryId, amount));
    }

    @Test
    public void creditEntryMadeEffective() throws Throwable {
        final Long entryId = 1L;
        final int amount = 100;
        final Date start = nov(2011, 12);
        final Date end = nov(2012, 12);// a year effective period

        fixture.given(new CreditEntryCreatedEvent(entryId, amount))
            .when(new MakeCreditEntryEffectiveCommand(entryId, start, end))
            .expectEvents(new CreditEntryMadeEffectiveEvent(entryId, start, end));
    }

    //omitted support methods
}

public class CreditEntry extends AbstractAnnotatedAggregateRoot {

    @AggregateIdentifier
    private Long id;
    private int amount;
    private Date effectiveDateRangeStart;
    private Date effectiveDateRangeEnd;
    private Status status;

    @CommandHandler
    public CreditEntry(CreateCreditEntryCommand command) {
        apply(new CreditEntryCreatedEvent(
            command.getEntryId(), command.getAmount()));
    }

    @EventHandler
    public void on(CreditEntryCreatedEvent event) {
        this.id = event.getEntryId();
        this.amount = event.getAmount();
        this.status = Status.NEW;
    }

    @CommandHandler
    public void markCompleted(MakeCreditEntryEffectiveCommand command) {
        apply(new CreditEntryMadeEffectiveEvent(
            command.getEntryId(), command.getStart(), command.getEnd()));
    }

    @EventHandler
    public void on(CreditEntryMadeEffectiveEvent event) {
        this.effectiveDateRangeStart = event.getStart();
        this.effectiveDateRangeEnd = event.getEnd();
        this.status = Status.EFFECTIVE;
    }

    public CreditEntry() {}

    public enum Status {
        NEW, EFFECTIVE, EXPIRED
    }
}

テスト コードにより、ドメイン モデルと axon-framework との統合コードを書くことができましたが、イベントによってどのような副作用が発生したかはカバーされていません。どこでテストしましたか? たとえば、有効にすると、与信入力のステータスは有効になります。他のテスト メソッドで CreditEntry インスタンスを作成し、特定の on(...Event event) メソッドを呼び出してテストする必要がありますか?

もう 1 つの質問は、ビジネス検証ロジックをどこに置くべきかということです。コマンドハンドラメソッドで?CreditEntry が既に有効になっている場合、再度有効にできない場合を想定します。

@CommandHandler
public void markCompleted(MakeCreditEntryEffectiveCommand command) {
    if (is(NEW)) {
        apply(new CreditEntryMadeEffectiveEvent(
            command.getEntryId(), command.getStart(), command.getEnd()));
    } else {
        throw new IllegalStateException(.......);
    }
}

どんなアイデアでも大歓迎です、ありがとう。

4

1 に答える 1

5

最初の質問について: 集計オブジェクトの内部状態に副作用があるということですか? Given-When-Then フィクスチャ テストは、集計を一種のブラック ボックスとして扱います。実際、内部状態をテストする必要はありません。適切なイベントが適用されることだけが重要です。

したがって、たとえば、決定ロジックは内部状態に依存しないため、フィールドのない集計になる可能性があります (ID を期待)。経験則として、後でどのイベントを適用するかを決定する必要がある場合、またはイベントで適用されるデータを変更する場合にのみ、イベントで転送されたデータを集約オブジェクトに保存します。

それを念頭に置いておけば、実際に内部状態をテストする必要はありません。特定の句で特定のイベントを使用して集計を構成し (いくつかの状態を構築)、コマンドを適用するだけです。正しいイベントが出てきたら...完了です。

2 番目の質問について: ビジネス検証はコマンド ハンドラーで行う必要があります。applyしたがって、メソッドが呼び出される前にすべてを検証する必要があります。この理由の 1 つ: 有効期間中に検証ロジックが変化するシステムを想像してみてください。ただし、システムの導入時に入力された古いデータを処理する必要があります。検証がイベント ハンドラーで行われ、検証がイベントが最初に導入されたときと同じでない場合、「古い」データが現在の検証ロジックと一致しないため、イベントからの集計の読み込みが失敗する可能性があります。

于 2013-12-03T13:56:46.100 に答える