EventSourcingRepository not working - no UoW started

I recently started playing with Axon Framework, so I’m kinda new here.

Using Axon in connection with CQRS (Standard Repositories/Projections) works great.
But can’t figure out how to use EventSourcingRepositories, tried multiply solutions I’ve found online and it works, but always has to use

unitOfWork = DefaultUnitOfWork.startAndGet(null);

in front of or any EventSourcingRepository solution I’ve found otherwise I’m getting Exception

No UnitOfWork is currently started for this thread.

unitOfWork = DefaultUnitOfWork.startAndGet(null);
EventSourcingRepository<Tutor> repository = EventSourcingRepository.builder(Tutor.class).eventStore(this.eventStore).build();
        Tutor tutor = repository.load("id").getWrappedAggregate().getAggregateRoot();

Thank you for your help.

Hey @Daniel_S, and welcome to the forum!

Wherever you’re interacting with Axon Framework’s infrastructure components, I would foremost recommend to use the interface.
Thus, when directly interacting with an aggregate repository (which is similarly not a necessity), I would use the Repository.

Concerning the UnitOfWork issues, that’s because Axon Framework expects an active UnitOfWork whenever any type of message handling function’s invoked. Similarly, Axon will generate a UnitOfWork when messages are dispatched.
In essence, the UnitOfWork manages the complete lifecycle of messages and thus is the cornerstone of the Framework’s functionality.

The aggregate is a specialized message handler, as it’s a group of handlers together.
So, when dealing with your Aggregate, Axon expects an active UnitOfWork.

You can, indeed, start the UnitOfWork manually.
However, if you use Axon’s message buses instead of invoking the Repository directly, you are ensured a UnitOfWork is started and dealt with as intended.

If you want to take CQRS a step further, you can thus dispatch a command on the CommandGateway/CommandBus that either (1) is handled by a function invoking the Repository or (2) directly invokes an annotated command handler on the Aggregate.
Simply put, you can do something like this:

Option1

public class CommandHandlingComponent {
    private Repository<SomeAggregate> repository;

    @CommandHandler
    public void handle(SomeCommand command) {
        repository.load(command.getAggregateId())
                  .execute(yourAggregate -> yourAggregate.handle(command));
    }
}

Option2

public class SomeAggregate {

    @CommandHandler
    public void handle(SomeCommand command) {
        // your command handling task here
    }
}

In doing either of the above, you’ve told Axon Framework firstly to dispatch a command on the bus and thus initiate a UnitOfWork.
Then the CommandBus is concerned with routing the message (so you don’t have to), invoking the @CommandHandler annotated method.

In the case of option1, you would thus still work with the Repository directly (if desired) and are ascertained a UnitOfWork is active (and managed correctly!).
When using option2, the same guarantee is in place, although that time around Axon Framework will invoke the Repository for you.

Hi @Steven_van_Beelen,
really appreciate your thorough reply.

Yes, I’ve tried Repository solution as well.

I was more thinking about trying a solution without Projections using Axon Server to get the state of the Aggregate in @Service, for the sake of simplicity:

@GetMapping(path = "/tutor")
public Tutor getTutor() {
   // unitOfWork = DefaultUnitOfWork.startAndGet(null);
   EventSourcingRepository<Tutor> repository = 
   EventSourcingRepository.builder(Tutor.class).eventStore(this.eventStore).build();
   Tutor tutor = repository.load("id").getWrappedAggregate().getAggregateRoot();

   return tutor;
}

I know it’s not a correct way, but just stick in my head what would be the way to do it without projections.

Thanks again for your reply.

I wouldn’t necessarily call it the right or wrong way.
Instead, you’d be doing CQRS purely on the API level and not on the model level.
Nothing inherently wrong with that, actually.

The definition of CQRS does not necessitate that you use distinct models, for example.
However, it is the bias that Axon Framework has chosen to provide the (we think) right level of separation, enabling more application flexibility.

Although, surely against assumed deduplication.
I’m calling it assumed, as the command and query models serve distinct purposes.

When taking this route, be sure to add all data to your model.
You cannot follow the regularly suggested approach of leaving the Aggregate for business validation only, as that’s simply not true if you reuse it for queries.

Now, back to the point of making your code work.
If, instead of invoking the Repository directly on a GET Mapping, you would use a query message, you can again benefit from Axon Framework automatically setting the UnitOfWork and managing it for you.
If you prefer to not use queries, constructing a DefaultUnitOfWork should be sufficient, although it is your task to close it again once you’re done. Otherwise, it’ll linger in your application and potentially cause weird behavior in other Axon Framework components.