Zon
Zon

Reputation: 19880

Axon State-Stored Aggregate Test IllegalStateException

PROBLEM: Customer technical limitations force me to use Axon with state-stored Aggregates in PostgreSQL. I try a simple JPA-Entity Axon-Test and get IllegalStateException.

RESEARCH: A simplified project on the case is available at https://gitlab.com/ZonZonZon/simple-axon.git

In my test on

fixture.givenState(MyAggregate::new)
    .when(command)
    .expectState(state -> {
      System.out.println();
    });

I get

The state of this aggregate cannot be retrieved because it has been modified in a Unit of Work that was rolled back java.lang.IllegalStateException: The state of this aggregate cannot be retrieved because it has been modified in a Unit of Work that was rolled back at org.axonframework.common.Assert.state(Assert.java:44)

QUESTION: How to test an aggregate state using Axon and escape the error?

Upvotes: 0

Views: 702

Answers (1)

Corrado Musumeci
Corrado Musumeci

Reputation: 306

there are some missing parts in your project to let the test run properly. I will try to tackle them as concisely as possible:

  • your Command should contain the piece of information that connects it to the Aggregate. @TargetAggregateIdentifier is the annotation provided by the framework that connects a certain field to its @AggregateIdentifier counterpart into your Aggregate. You can read more here https://docs.axoniq.io/reference-guide/implementing-domain-logic/command-handling/aggregate#handling-commands-in-an-aggregate. Said so, a UUID field needs to be added to your Create command.

  • This information will be then passed into the Created event : events are stored and can be processed both by a replay or an Aggregate re-hydration (upon client’s restart). (These) are the source of truth for our information.

  • @EventSourcingHandler annotated method will be responsible for applying the event and updating the @Aggregate values

    public void on(Created event) {
         uuid = event.getUuid();
         login = event.getLogin();
         password = event.getPassword();
         token = event.getToken();
    }
    
  • the test will then look like

    public void a_VideochatAccount_Created_ToHaveData() {
    
       Create command = Create.builder()
                              .uuid(UUID.randomUUID())
                              .login("123")
                              .password("333")
                              .token("d00a1f49-9e37-4976-83ae-114726938c73")
                              .build();
    
       Created expectedEvent = Created.builder()
                                     .uuid(command.getUuid())
                                     .login(command.getLogin())
                                     .password(command.getPassword())
                                     .token(command.getToken())
                                     .build();
    
       fixture.givenNoPriorActivity()
              .when(command)
              .expectEvents(expectedEvent);
    }
    

This test will validate your Command Part of your CQRS.

I will then suggest to separate the Query Part from your @Aggregate: you will then need to handle events with @EventHandler annotation placed on a method into a Projection @Component class, and implement the piece of logic that will take care of storing the information in the form that you need into PostgreSQL @Entity, using the @Repository JPA way, which I am sure you are familiar with. You can find useful information on the ref guide https://docs.axoniq.io/reference-guide/implementing-domain-logic/event-handling following the video example on The Query Model based on code that you can be found in this repo https://github.com/AxonIQ/food-ordering-demo/tree/master

Hope that all is clear,

Corrado.

Upvotes: 1

Related Questions