Thank you very much this not making things more confusing. This is clearing up a lot of details here. If you allow, I would like to continue on here. I hope this discussion is relevant to more new Axon users as well.
Perhaps the easiest way would be to register a custom CommandCallback at the CommandGatewayFactory that logs each failed command (using e.g. XStream) as well as the stack trace of the exception. Ideally also log some context like the aggregate identifier and sequence number of the aggregate at the time of invalidation. In Axon 2 you can also use an AuditLogger for this (if you’re on Axon 3 things are even easier – in that case let me know).
We are at the early stages of project I am running with students. So we are not in production and go for the bleeding edge and use the 3.0 build as they become available. So basically my questions currently proxy 15 students I am taking with me on the Axon adventure.
So yes, please elaborate on the 3.0 solution to this.
Ideally the aggregate does not need to concern itself with structural validation of commands. It should only make sure that a given command is valid given the state of the aggregate.
I completely agree with this view. And I now know why the experiements failed. We did not consider the difference in handling checkes vs unchecked exceptions and basically turned structural valifation into IllgalArgumentExceptions. Thus the UoWs were rolled back as you describe below. Makes sense now.
I therefore do not consider a structurally faulty command to be a case where a command handler exception is to be expected. I’m not against structural validation using a dispatch interceptor (i.e. after command construction). Consider the following:
- Object command = new RenameCommand(…);
If the command is structurally invalid (e.g. the newName is too long) I don’t care if an exception is raised in line 1 or line 2 as long as the exception is raised before the command is handled by the aggregate. A dispatch interceptor will throw its exception before the command is dispatched to the command handler, i.e. in the thread that sends the command, so this would work as well.
Very good. Me “not taking into considerarion checkesd vs unchecked” made me worry about a few later steps I had planned with regards to authorization. Now that I know that I can cleanly handle thise cases with custom “AccessDeniedExceptions” from an authorization command interceptor I am feeling much better.
a) RenameCommand(AggregateId id, Name newName)
b) RenameCommand(String id, String newName)
I also agree that a) is preferred, but mostly because it leads to more readable code and less chance to mix up the order of constructor parameters.
Yes. I always felt this way too. It is also much more in line with DDD principles, making the ubiquitous language the way to express commands instead of primitives where you have to translate the ubiquitous language into technical concepts isntead of usuing it directly.
There were two factors leading to me trying out the “lets try out what happens if we use primitives only”:
- recent Axon code examples were only using primitives.
- I went through some online discussions on Value objects, Event, Commands, CQRS. And there is a (from my point of view correct) concern, that using Value objects may result in leaking domain logic into the commands/events.
Based on these recent experiences I would argure that you should use Domain specific Value objects in Commands/Events, as they make code easier to read, and less error prone. BUT you should take care not to use objects that are more than just sumb data containers with more than simple structural validation to not leak domain logic into the events/commands zipping around the busses and into the event store.
By default Axon does not roll back the UoW if the exception is a checked exception.
Once the command has been structurally validated (outside the aggregate) I don’t think this situation is very common as I wrote in my last email. However, in those rare cases that the saga needs to know why its command was rejected it may seem preferable to use a failure event instead of an exception. What I don’t like about failure events from the aggregate however, is that the aggregate is then forced to publish events it doesn’t care about.
I have to agree. It felt bad to define all these new events to be able to disambiguate between the different failures. It clogs up the Aggregate API, command handlers, and hides the ubiquitous language which is actually to be expressed be the commands.
In fact the only component that cares is the dispatching saga. An exception seems cleaner, especially as these are such rare cases. If you don’t want to block the saga I liked Steve’s suggestion to dispatch the command and collect the result in a CommandCallback. If the command fails publish an event meant for the saga (or load the saga and invoke a recovery method on the saga). If you’re using Axon 2 publish the event using the EventTemplate.
What I was doing now is to inject the EventBus in the respective handler and to dispatch the failure event there. And I think the notiion that “only the one Saga cares about this” is actually a good indicator for the idea to use an exception in this case. Only use an event, if there is actually another component which is interested.
The reason why I feel a bit uneasy about the callback is, that I do not yet have 100% grip on the saga behaviour in this case. I was wondering, if it may be that I may not make any assumption about when the command will be handled. And also I do not yet understand 100% when a Saga is put at rest to be persisted. So I was askingmyself what happens, if I register an anonymous callback class, and for some reason the infrastructure decides to persist the saga. My assumption was that to be sure that the saga actually survives odd situations may be to always use events.
As we’ve seen, simply throwing an exception covers all cases. Failure events are not required at the aggregate level.
Will probably make the code much cleaner. The events dispatching also created alot of boilerplate in the command handlers.
Now an example for where failure is expected is ChangeUsernameCommand where some index for claimed usernames is consulted and a Failure Event is created if the username is previously claimed.
By the way, this specific example may be one where the aggregate should not validate at all. Before dispatching the command check if the username is taken, if so throw an exception before dispatching the command to the handler. If two users still select the same username (simultaneously) the application should be able to roll back the second command. A unique index on the username column in the query side may be enough to initiate the rollback. The advantage of this approach is that the User entity handling the command is unaware of other User entities, which is probably for the best.
For simple application the approach you describe is sufficient. I am actually forking on a domain model where authentication/authorization is the core bounded contest and not a simple simple generic support context. So for me this has to be 100% tight.
After a lot of experimentation I came to the following conclusions:
at no point may any query model change a value object which has a “must be unique across a set of aggregates” (such as the username). In the case of a security domain, I cannot consider the attempt to change it to an existing one “a rare exception”. I consider this a deliberate attack scenario. Any moment where some of the query models becomes inconsistent with regards to such constraints my be a vounerability. It is just fine to have eventual consistency overall, and that it takes some time for updates to probagate.
Example: I may have two query models for different voews in the application where I have a column with “username”. While it is possible to to enforce the “unique” constraint on each of the models, it becomes at least a little complex to reason about potential race consitions, and also the aggregate itself may be in an inconsistent state with regards to username uniqueness until the view models have detected the violation upon update. Also you suddenly have to deal with the rollback. And in an attack szenario where this most likely this will not be a rare occasion, but a forced occurence.
I think: The aggregate may only change when the uniqueness guaranteed. There may never be inconsisten change events that propagate to the query models.
- Introduce an additional simple index in the domain model itself.
- Creation of the aggregate and for changing the value with the uniqueness constraint reside in a service and not within the aggregate in order to have access to the index.
- The command handler tries to claim the value before instanitating the aggregate or calling the “changeValue” method of the aggregate.
- Result: The aggregate will only ever chreate “UsernameChangedEvent” when there is no conflict.
This does not mean that I do not first check if the username is already claimed before dispatching the command. It is just a) the paranoid implementation b) keeps the logic within the aggregate clean c) avoids messy rollback protocols. c) The query models only ever get consistent updates.
I also consider it bad form have acritical part of the domain consistency logic to rely on query models. It is better to enforce this within the domain logic part of the code, before it propagates to the query models. I think I have actually implemented most of the possible versions of this and tried them. And this is my current stance of the use-case.
Hope this helped and didn’t make things more confusing :).
Again, super helpful. Thanks a lot for taking the time to answer.