Not bad. I’ve seen a lot of these ES domain implementations lately.
If you want to stick strictly to the design patterns, I’d probably take Save() off the repository and move it to a new class of type EventStoreUnitOfWork that implements a simple IUniwOfWork.Commit() method instead.
The unit of work can then coordinate access to the repository, track changes to the aggregate and be responsible for writing changes (events) back to ES.
Aggregate changes can be tracked using event handlers or just passing an Action to an aggregate during construction.
The unit of work can resolve any concurrency issues itself or it can delegate back to the aggregate if needed. The original command could even provide some hint how to resolve a concurrency issues (ignore, retry, etc)
Commit can flush a collection of raised events back to ES, or Rollback (basically do nothing).
That keeps the aggregate nice and clean and your command handlers end up being, at most, 3 lines of code.