CQRS code duplication in commands

醉酒当歌 提交于 2019-12-06 12:30:31

Why not refactor your Command Handler into one that implements multiple interfaces:

public class UserCommandHandler : CommandHandlerBase, 
                                  IHandle<ResetPasswordCommand>,
                                  IHandle<UpdateLastLoginCommand>
{
    public void Execute(ResetPasswordCommand command)
    {
        var user = GetUserByEmail(command.EmailAddress);

        user.EmailAddress = command.EmailAddress;
        ...
        db.Save();
   }

    public void Execute(UpdateLastLoginCommand command)
    {
        var user = GetUserByEmail(command.EmailAddress);

        user.LastLogin = DateTime.Now;
        ...
        db.Save();
   }

   private User GetUserByEmail(string email) {
            return (from c in db.Users
                   where c.EmailAddress = command.EmailAddress
                   select c).FirstOrDefault();
   }
}

This way, you can refactor private helper methods within the command handler, you command handler can handle similar commands, and you reduce your code duplication. You also won't need the "god" repository.

Personally, I'd rather have the private GetUserByEmail helper as a separate query class that I inject the db context in through the constructor, so that GetUserByEmail is a very specific class that gets me a User.

Hope this helps.

It won't create a god repository . It will be a proper repository having a method used by a command use case. But this implies you're using the correct Repository Pattern, which means no IQueryable or EF exposed. Remember that a domain repository has 'query' methods needed only by the domain use cases and the repository only deals with domain aggregates roots (whole domain objects).

Your current approach is to use EF ( a DAO in the end) in your application services and this means the application layer and probably the domain layer as well are coupled to EF. Your high level services (a command handler is a service in the end, implementing a use case) shouldn't do queries because from their point of view there is no rdbms i.e they don't know about queries.

If your app is simple enough or you know it won't change much in the future, then you can take the shortcut of using directly EF, but if you decide to simplify things this way, then you don't need CQRS and a message based architecture.

I know it looks like code duplication, I know it looks like you're going against the DRY principals but i can assure you using shared service repository code in behaviours is never a good idea. One of the problems is that each behaviour could actually require slightly different implementations of the GetUser by email call. One my require the full name the other may not. By sharing this code you are effectively tightly coupling both calls. One behaviour may now require an extra piece of data returned but now as you've tighltly coupled you call whenver the other implementation calls this 'shared service' it has the overhead of all that extra data it doesn't require.

If you want to share code when performing repository calls use something called the specification pattern and / or the strategy pattern. You don't get any of the nasty tight coupling problems with the example above and, as a bonus, your code will read better as intentions are at the fore rather than implementations.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!