This type of validation should occur in the command handler for the EndMonthCommand (event sourcing handlers should only set/update the aggregate state); try moving it there and throw an exception if validation fails.
Short answer - validate the message using expectExceptionMessage(String).
Slightly longer answer - “exception details” is something that is used when you have a distributed application scenario; when a command handler in one process receives a command from a client running in another process, any exceptions that are thrown must be of type CommandExecutionException, wherein you can provide your custom details to serialize & send back to the client.
Say you’re performing “balance” validation as in your case; in the non-distributed scenario you could simply throw the EndBalanceDoesNotMatchCurrentBalanceException; in the distributed scenario you’d have to do something like the following:
// in the command handler.
if (currentBalance != event.endBalance) {
throw EndBalanceDoesNotMatchCurrentBalanceException(
ledgerId = event.ledgerId,
currentBalance,
event.endBalance,
)
}
// elsewhere in the aggregate class.
@ExceptionHandler
public void handle(EndBalanceDoesNotMatchCurrentBalanceException ex) {
throw new CommandExecutionException(
"An error occurred during command handling",
ex,
// these are the exception details to possibly validate.
mapExceptionToDetails(ex)
);
}