I am currently working on a project that uses clean architecture with CQRS design pattern. I have created IUserManagerWrapper interfact in the Application layer and implemented that in the Infrastructure layer.
Can i access directly IUserManagerWrapper
from the controller or Should I create separate Queries(as I mentioned I am using CQRS design pattern) for each operation(i.e.FindUserByNameQuery
, GetRolesQuery
, and CheckPasswordQuery
)?
I am not sure how should I access IUserManagerWrapper
from my controller without violating the principles of clean architecture.
User Manager Wrapper:
namespace JwtWithIdentityDemo.Application.Abstractions.Authentication
{
public interface IUserManagerWrapper
{
Task<IdentityResult> CreateUserAsync(IdentityUser user, string password);
Task<IdentityUser> FindByNameAsync(string userName);
Task<bool> CheckPasswordAsync(IdentityUser user, string password);
Task<IList<string>> GetRolesAsync(IdentityUser user);
}
}
User Manager implementation in Infrastructure layer:
namespace JwtWithIdentityDemo.Infrastructure.Authentication
{
public class UserManagerWrapper : IUserManagerWrapper
{
private readonly UserManager<IdentityUser> _userManager;
public UserManagerWrapper(UserManager<IdentityUser> userManager)
{
_userManager = userManager;
}
public async Task<IdentityResult> CreateUserAsync(IdentityUser user, string password)
{
return await _userManager.CreateAsync(user, password);
}
public async Task<IdentityUser> FindByNameAsync(string userName)
{
return await _userManager.FindByNameAsync(userName);
}
public async Task<bool> CheckPasswordAsync(IdentityUser user, string password)
{
return await _userManager.CheckPasswordAsync(user, password);
}
public async Task<IList<string>> GetRolesAsync(IdentityUser user)
{
return await _userManager.GetRolesAsync(user);
}
}
}
Should I create separate Query Handlers? For example Check Password Query Handler created below:
namespace JwtWithIdentityDemo.Application.Queries.Users
{
public class CheckPasswordQueryHandler : IRequestHandler<CheckPasswordQuery, bool>
{
private readonly IUserManagerWrapper _userManager;
public CheckPasswordQueryHandler(IUserManagerWrapper userManager)
{
_userManager = userManager;
}
public async Task<bool> Handle(CheckPasswordQuery request, CancellationToken cancellationToken)
{
var result = await _userManager.CheckPasswordAsync(request.User, request.Password);
return result;
}
}
}
I recommend having Features at the Application layer, this Features will be composed by Command and Queries (To allow easy segregation of responsibilities and resources), this Features will consume the Managers (could be also Internal or External Services) and they will be called by the Controller using Mediator Pattern (even you could add Fluent Validation), you will have a structure like this:
-> Application
--> Features
---> Commands
----> AddEditUserCommand
----> DeleteUserCommand
---> Queries
----> CheckPasswordCommand
----> GetUserByIdCommand
----> GetAllUsersCommand
You’re applying a good practice with the Handlers at “Features” level, following with the previous command you could have something like this:
public partial class CheckPasswordCommand : IRequest<bool>
{
public int Id { get; set; }
[Required]
public string User { get; set; }
[Required]
public string Password { get; set; }
}
internal class CheckPasswordCommandHandler : IRequestHandler<CheckPasswordCommand, bool>
{
private readonly IUserManager _userManager; // You could inject your Manager here
private readonly IStringLocalizer<CheckPasswordCommandHandler> _localizer;
public CheckPasswordCommandHandler(IUserManager userManager, IStringLocalizer<CheckPasswordCommandHandler> localizer)
{
_userManager = userManager;
_localizer = localizer;
}
public async Task<bool> Handle(CheckPasswordCommand command, CanUserationToken canUserationToken)
{
try{
var result = await _userManager.CheckPasswordAsync(request.User, request.Password);
return result;
}
catch{
// Log Error and get a proper fail response.
return FailResponse;
}
}
}
This implementing Mediator Pattern, in this case with (MediatR):
public class UserController : CustomAbstractApiController<UserController>
{
[AllowAnonymous]
[HttpPost]
public async Task<IActionResult> CheckPasswordAsync(CheckPasswordCommand command)
{
return Ok(await _mediator.Send(command));
}
}
And an abstract controller to inject Mediator:
[ApiController]
[Route("api/v{version:apiVersion}/[controller]")]
public abstract class CustomAbstractApiController<T> : ControllerBase
{
private IMediator _mediatorInstance;
protected IMediator _mediator => _mediatorInstance ??= HttpContext.RequestServices.GetService<IMediator>();
}
Hope you find this information useful, feel free to ask.