Either you are really good and get things right without much help, which isn't often the case, or you end up in two other directions.
You over-engineer with DDD/CQRS/ES.
You under-engineer without them.
Most developers I met preferred the last solution, probably because they end up with their "own" mess and not the mess of some process.
And I have to admit, the only people I met, that used these techniques, were some "digital transformation" consultants who made their living by selling this stuff to big corps, which isn't too convincing either.
CQRS/ES definitely seems like a correct foundations for a distributed system, I just don't think we have the right abstractions to properly model this without wanting to stab yourself in the eye.
In a client-server system like a web API where you want to respond to the client as fast as possible having to reconstruct your entity from snapshot + some history can take some time. So you end-up with corrective events and responses which are "we have noted your request, poll us to know the result when it's done". The worst is the ES system is already in use in your RDBMS if you use one. Most CQRS + ES demonstration are done with the simple happy path. Rarely something like user A changes the role of user B. User B was saving changes on a document they can't access anymore: what happen?
Now with software on your PC or with a constant link to the server things feel a lot more useful.
For your example, here are two approaches depending on your implementation and your tolerance for edge cases and eventual consistency:
- you can enter into a saga which would do a two phase commit, ensuring that the user is indeed authorized when the edit is made. All possible states are captured in your event model and so your event history will always show what has happened
- you can load the document aggregate when you process the command and query it at that time, throwing an exception if the user is not authorized and then you can notify the user or send another command. In this case, your event model does not have to include events that describe the violation of domain rules
The first option gives you the guarantee you are looking for, but comes at the cost of complexity. The second option will give you the correct results unless a user is deauthorized before the edit command completes. Some systems process all commands in serial and so it would not be a problem. Some systems partition their commands to distribute the work and so this edge case can occur.
But you can select what makes most sense for you.
So basically don't introduce race conditions that aren't inherently part of the domain. This seems like a modelling failure to me.