I've been playig around with using seperate query and command classes instead of using repositories with multiple methods.
Your UserRepository might a GetByEmail(string email) method. Instead of that, I have a GetUserByEmailQuery class and a GetUserByEmailQueryHandler class.
Your UserRepository might have a UpdatePassword(string newPassword) method. Instead of that, I have a UpdateUserPasswordCommand class and a UpdateUserPasswordCommandHandler class.
Everything comes together in my controllers through a QueryAndCommandDispatcher.
First some interfaces:
public interface IQueryHandler<in TQueryData, out TResult> where TQueryData : class { TResult Execute(TQueryData query); } public interface ICommandHandler<in T> where T : class { void Handle(T command); } public interface IQueryAndCommandDispatcher { void ExecuteCommand<T>(T command) where T : class; TResult ExecuteQuery<TQueryData, TResult>(TQueryData query) where TQueryData : class; }
As you can see, a query is supposed to return something while a command isn't. A command should change data, a query shouldn't.
Here's how that query to get a user by his or her email would look like (IDataContext is an interface describing my custom DbContext):
public class GetUserByEmailQuery { public string Email { get; private set; } public GetUserByEmailQuery(string email) { Email = email; } } public class GetUserByEmailQueryHandler : IQueryHandler<GetUserByEmailQuery, User> { private readonly IDataContext dataContext; public GetUserByEmailQueryHandler(IDataContext dataContext) { this.dataContext = dataContext; } public User Execute(GetUserByEmailQuery query) { return dataContext.Users.First(u => u.Email == query.Email); } }
Here's how that command to update a user's password would look like:
public class UpdateUserPasswordCommand { public int UserId { get; private set; } public string Password { get; private set; } public UpdateUserPasswordCommand(int userId, string password) { UserId = userId; Password = password; } } public class UpdateUserPasswordCommandHandler : ICommandHandler<UpdateUserPasswordCommand> { private readonly IDataContext dataContext; public UpdateUserPasswordCommandHandler(IDataContext dataContext) { this.dataContext = dataContext; } public void Handle(UpdateUserPasswordCommand command) { var user = dataContext.Users.Find(command.UserId); user.Password = command.Password; dataContext.SaveChanges(); } }
And here's the implementation of the IQueryAndCommandDispatcher interface:
public class QueryAndCommandDispatcher : IQueryAndCommandDispatcher { private readonly IKernel kernel; public QueryAndCommandDispatcher(IKernel kernel) { this.kernel = kernel; } public void ExecuteCommand<T>(T command) where T : class { var handler = kernel.Get<ICommandHandler<T>>(); handler.Handle(command); } public TResult ExecuteQuery<TQueryData, TResult>(TQueryData query) where TQueryData : class { var handler = kernel.Get<IQueryHandler<TQueryData, TResult>>(); return handler.Execute(query); } }
And everything comes together in a controller like this:
public class HomeController : Controller { private readonly IQueryAndCommandDispatcher dispatcher; public HomeController(IQueryAndCommandDispatcher dispatcher) { this.dispatcher = dispatcher; } [HttpGet] public ActionResult Index(string email) { var userQuery = new GetUserByEmailQuery(email); var user = dispatcher.ExecuteQuery<GetUserByEmailQuery, User>(userQuery); return View(user); } [HttpPost] public ActionResult Update(int userId, string password) { var updateCommand = new UpdateUserPasswordCommand(userId, password); dispatcher.ExecuteCommand(updateCommand); return View(); } }
This reduces the number of dependencies your controllers need while still keeping everything testable.