CQRS: Should the ICommandHandler<TCommand> be responsible for calling SaveChanges() on the IUnitOfWork?

StackOverflow https://stackoverflow.com/questions/23623699

  •  21-07-2023
  •  | 
  •  

Question

Let's say I have a command handler:

public class AddNewUserCommandHandler : ICommandHandler<AddNewUserCommand>
{
    //IUserManagementUnitOfWork implements IUnitOfWork.SaveChanges()
    //hold references to related repositories
    //basically an abstraction over DbContext/DbSet
    private IUserManagementUnitOfWork uow; 

    public AddNewUserCommandHandler(IUserManagementUnitOfWork uow)
    {
        this.uow = uow;
    }

    public void Handle(AddNewUserCommand command)
    {
        var user = new User
        {
            FirstName = command.FirstName,
            LastName = command.LastName
        };
        uow.UserRepository.Add(user);
        uow.SaveChanges(); //should this be here?
        command.UserId = user.Id;
    }
}

My question is: should the ICommandHandler<TCommand> be responsible for calling SaveChanges() on the IUnitOfWork? or should this be the responsibility of the caller?

For example, the AddNewUserCommandHandler would only have the IRepository<User> as its dependency, and the caller would hold reference to the IUnitOfWork and call SaveChanges() when it feels like it. The caller might be an ICommandProcessor that can process multiple ICommands and call SaveChanges when all commands are handled by its handlers successfully.

Or, leave the SaveChanges() duty to the ICommandHandler<TCommand> and wrap the batch command processor in a TransactionScope, and delegate command.UserId = user.Id to an event that will execute after calling TransactionScope.Complete().

Which approach is the better one?

Thanks!

Was it helpful?

Solution

I've implemented a setup that's quite similar to the ddd-cqrs-sample. There's a class for the "run environment" that's responsible for running commands with the corresponding command handlers. Besides this my RE implements the transaction boundary. It's responsible for starting a unit of work and do a commit/rollback after command execution depending on the command's result.

Of course this depends on your use case. If your commands behave much the same, perhaps your calling class can make up the transaction boundary. I think in most cases one should try to establish such a command design in order to have commands that are simple and focussed on one task.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top