Question

I have an issue where I would like my handler to use data generated from the handlers:

  1. UpdateUserProfileImageCommandHandlerAuthorizeDecorator
  2. UpdateUserProfileImageCommandHandlerUploadDecorator
  3. UpdateUserProfileImageCommandHandler

My problem is both architectural and performance.

UpdateUserCommandHandlerAuthorizeDecorator makes a call to the repository (entityframework) to authorize the user. I have other decorators similar to this that should use and modify entities and send it up the chain.

UpdateUserCommandHandler should just save the user to the database. I currently have to make another repository call and update the entity while I could have worked on the entity from the previous decorator.

My issue is that the command only accepts the user Id and some properties to update. In the case where I get the user entity from the Authorize decorator, how can I still work on that entity up the chain? Is it Ok to add that User property to the command and work on that?

Code:

public class UpdateUserProfileImageCommand : Command
{
    public UpdateUserProfileImageCommand(Guid id, Stream image)
    {
        this.Id = id;
        this.Image = image;
    }

    public Stream Image { get; set; }

    public Uri ImageUri { get; set; }
}

public class UpdateUserProfileImageCommandHandlerAuthorizeDecorator : ICommandHandler<UpdateUserProfileImageCommand>
{
    public void Handle(UpdateUserProfileImageCommand command)
    {
         // I would like to use this entity in `UpdateUserProfileImageCommandHandlerUploadDecorator`
         var user = userRespository.Find(u => u.UserId == command.Id);

         if(userCanModify(user, currentPrincipal))
         {
             decoratedHandler(command);
         }

    }
}

public class UpdateUserProfileImageCommandHandlerUploadDecorator : ICommandHandler<UpdateUserProfileImageCommand>
{
    public void Handle(UpdateUserProfileImageCommand command)
    {
         // Instead of asking for this from the repository again, I'd like to reuse the entity from the previous decorator
         var user = userRespository.Find(u => u.UserId == command.Id);

         fileService.DeleteFile(user.ProfileImageUri);

         var command.ImageUri = fileService.Upload(generatedUri, command.Image);

         decoratedHandler(command);       

    }
}

public class UpdateUserProfileImageCommandHandler : ICommandHandler<UpdateUserProfileImageCommand>
{
    public void Handle(UpdateUserProfileImageCommand command)
    {
         // Again I'm asking for the user...
         var user = userRespository.Find(u => u.UserId == command.Id);

         user.ProfileImageUri = command.ImageUri;     

         // I actually have this in a PostCommit Decorator.
         unitOfWork.Save();
    }
}
Était-ce utile?

La solution

You should not try to pass on any extra data just for the sake of performance. Besides, usng decorators, you can't change the contract. Instead you should allow that user entity to be cached and this should typically be the responsibility of the repository implementation. With Entity Framework this is actually rather straightforward. You can call DbSet.Find(id) and EF will first look up the entity in the cache. This prevents unneeded round trips to the database. I do this all the time.

So the only thing you have to do is add a Find(key) or GetById method to your repository that maps to EF's Find(key) method and you're done.

Furthermore, I agree with Pete. Decorators should be primarily for cross-cutting concerns. Adding other things in decorators can be okay sometimes, but you seem to split up the core business logic over both the handler and its decorators. Writing the file to disk be longs to the core logic. You might be conserned about adhering to the Single Responsibility, but it seems to me that your splitting a single responsibility out over multiple classes. That doesn't mean that your command handlers should be big. As Pete said, you probably want to extract this to a service and inject this service into the handler.

Validating the authorization is a cross-cutting concern, so having this in a decorator seems okay, but there are a few problems with your current implementation. First of all, doing it like this will cause you to have many non-generic decorators, which leads to a lot of maintenance. Besides, you silently skip the execution if the user is unauthorized which is typically not what you want.

Instead of silently skipping, consider throwing an exception and prevent the user from being able to call this functionality under normal circumstances. This means that if an exception is thrown, there's either a bug in your code, or the user is hacking your system. Silently skipping without throwing an exception can make it much harder to find bugs.

Another thing is that you might want to consider is trying to implement this authorization logic as generic decorator. For instance have a generc authorization decorator or validation decorator. This might not always be possible, but you might be able to mark commands with an attribute. For instance, in the system I'm currently working on we mark our commands like this:

[PermittedRole(Role.LabManagement)]

We have a AuthorizationVerifierCommandHandlerDecorator<TCommand> that checks the attributes of the command being executed and verifies whether the current user is allowed to execute that command.

UPDATE

Here's an example of what I think your UpdateUserProfileImageCommandHandler could look like:

public class UpdateUserProfileImageCommandHandler 
    : ICommandHandler<UpdateUserProfileImageCommand>
{
    private readonly IFileService fileService;

    public UpdateUserProfileImageCommandHandler(IFileService fileService)
    {
        this.fileService = fileService;
    }

    public void Handle(UpdateUserProfileImageCommand command)
    {
         var user = userRespository.GetById(command.Id);

         this.fileService.DeleteFile(user.ProfileImageUri);

         command.ImageUri = this.fileService.Upload(generatedUri, command.Image);

         user.ProfileImageUri = command.ImageUri;     
    }
}

Autres conseils

Why do this via decorators in the first place?

Validation

The normal approach is to have clients do any and all validation required before submitting the command. Any command that is created/published/executed should have all (reasonable) validation performed before submitting. I include 'reasonable' because there are some things, like uniqueness, that can't be 100% validated beforehand. Certainly, authorization to perform a command can be done before submitting it.

Split Command Handlers

Having a decorator that handles just a portion of the command handling logic, and then enriches the command object seems like over-engineering to me. IMHO, decorators should be use to extend a given operation with additional functionality, e.g. logging, transactions, or authentication (although like I said, I don't think that applies for decorating command handlers).

It seems that uploading the image, and then assigning the new image URL in the database are the responsibility of one command handler. If you want the details of those two different operations to be abstracted, then inject your handlers with classes that do so, like IUserimageUploader.

Generally

Normally, commands are considered immutable, and should not be changed once created. This is to help enforce that commands should contain up front all the necessary information to complete the operation.

I'm a little late here, but what I do is define a IUserContext class that you can IoC inject. That way you can load the important user data once and then cache it and all other dependencies can use the same instance. You can then have that data expire after so long and it'll take care of itself.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top