I commited a bunch of files to the Axon Auction Example but I got
stucked...
In the JavaDoc of Repository's save method there is a hint:
"You are recommended to use a UnitOfWork instead of calling
<code>save()</code> explicitly."
But if I don't call "save(..)" explicitly nothing get's stored...
I checked the context file, and everything looked absolutely fine. I did notice that you are using Axon 0.6. Do you prefer to work on a stable release? The 0.7 version has some nice features that might make things easier. Spring Namespace support, for example.
Anyway, I checked the command handler too. That’s where I discovered something. You should call repository.add(aggregate) to let the repository know that you have created a new aggregate.
So, after:
User user = new User(…);
you call:
repository.add(user);
The API of you command handler gives me the impression that you have the intent to return a result for most -if not all- the command. You should always try to model the process in such a way that you can use fire-and-forget commands. A good command never fails.
I did notice that you are using Axon 0.6. Do you prefer to work on a stable
release? The 0.7 version has some nice features that might make things
easier. Spring Namespace support, for example.
Hmm... Is there a Maven Snapshot available?
Anyway, I checked the command handler too. That's where I discovered
something. You should call repository.add(aggregate) to let the repository
know that you have created a new aggregate.
OK, I'll try that.
The API of you command handler gives me the impression that you have the
intent to return a result for most -if not all- the command. You should
always try to model the process in such a way that you can use
fire-and-forget commands. A good command never fails.
Sounds good but you will always have at least some error conditions. I
don't want to transport Exceptions from the Command Server to the
Client as I want to keep the Command API as language agnostic as
possible. As you may have noticed I used the Hessian as transport
protocol. This way other languages that may not even have the concept
of an Exception can use the command service. But of course you are
right, there won't be (many) other scenarios beyond the "Create
Object" case that should transport additional data.
> I did notice that you are using Axon 0.6. Do you prefer to work on a stable
> release? The 0.7 version has some nice features that might make things
> easier. Spring Namespace support, for example.
I switched to the new 0.7-SNAPSHOT and found the source attached to it
is still 0.6
When you say “beyond the Create Object” commands, do you mean that you always expect a return value on those commands? You’re probably returning the generated identifier there. In my projects, I use client-generated identifiers. We only have a single command that has a return value in the entire application. And the only reason it’s there is that my creativity is too limited to find a way around it.
I think it's all more related to giving a clear error code & message.
In the Axon Auction Example we have for example a user registering
with a user id (Something like "peter123"). If the user id is already
in use by another user the command will fail.
The other point is creating a new aggregate - Returning the UUID (or
with Axon 0.7 the new "AggregateIdentifier" with a Create Command
seems very natural to me. In fact this is the only kind of return
value I see at the moment for command execution (beyond above error
information). Creating some kind of query just to return a new
identifier seems to be overkill and generating it client side prevents
a central generation of the value.
We have the policy in our application that the client is responsible for sending a command that makes sense. That means the client should do a query to check for uniqueness of a username. It is then able to display nice error messages to the user. In our case, a failing command throws an exception, but it is also perfectly possible to publish an UnableToCreateUserEvent (an application event, I guess, because no state was changed). In the end, it’s a matter of taste, really.
When i say “client generated id’s”, I don’t mean an ID that the client asks by doing a query. I mean that the clients comes up with an identifier on its own. That’s why I strongly believe in UUID’s. The client just say “create an aggregate for me with ID: UUID.randomUuid()”. The client sends the command and can (parallel) show the details of the newly created aggregate in the details screen, for example. No need to wait for a response from the server there. That increases perceived performance of your application dramatically.