How to avoid using Service Locator in my Unit Of Work with Delimited Contexts and Mediatr?

Asked

Viewed 32 times

0

Hello,

I am starting studies on Ddds and delimited contexts and would like to avoid the use of Service Locator to access my repositories through the Unit of Work.

I am currently using Mediatr to receive a Query message and resolve it (it is an API project):

    [HttpGet]
    public async Task<ActionResult<UsuarioDTO>> Get([FromQuery]PegarUsuarioPorNomeDeUsuarioQuery nomeDeUsuarioQuery)
    {
        try
        {
            UsuarioDTO usuario = await _Mediator.Send(nomeDeUsuarioQuery);
            if (usuario == null)
                return NotFound("Usuário não encontrado");
            else
                return Ok(usuario);
        }
        catch (Exception ex)
        {
            return BadRequest(ex.Message);
        }
    }

Handler for this Query is in my Security Context (a library class):

namespace SegurancaBC.Handlers.Queries
{
    public class PegarUsuarioPorNomeDeUsuarioHandler : IRequestHandler<PegarUsuarioPorNomeDeUsuarioQuery, UsuarioDTO>
    {
        private readonly IUnitOfWork _UoW;

        public PegarUsuarioPorNomeDeUsuarioHandler(IUnitOfWork uow)
        {
            _UoW = uow;
        }

        public async Task<UsuarioDTO> Handle(PegarUsuarioPorNomeDeUsuarioQuery request, CancellationToken cancellationToken)
        {
            IUsuarioRepository usuarioRepository = _UoW.PegarRepositorio<IUsuarioRepository>(); // implementado com Service Locator internamente
            UsuarioDTO usuario = await usuarioRepository.CarregarUsuario(new Email(request.NomeDeUsuario));
            return usuario;
        }
    }
}

This makes me worried, because "Pegarrepositorio" uses a Locator service to invoke the correct implementation of Iusuariorepository that I have defined in the Infrastructure layer.

Right now I have a project Sharedkernel with an interface to the Uow:

namespace SharedKernel.Repositories
{
    public interface IUnitOfWork
    {
        void Begin();
        void Commit();
        void RollBack();
        TRepositorio PegarRepositorio<TRepositorio>() where TRepositorio : IRepository;

        IDbConnection Connection { get; }
        IDbTransaction Transaction { get; }
    }
}

Below the specific implementation with Service Locator:

    public TRepositorio PegarRepositorio<TRepositorio>() where TRepositorio : IRepository
    {
        Type tipoRepositorio = typeof(TRepositorio);
        return (TRepositorio)_ServiceProvider.GetService(tipoRepositorio);
    }

In each Context I have different Repositories, such as:

namespace SegurancaBC.Domain.Repositories
{
    public interface IUsuarioRepository : IRepository
    {
        Task<UsuarioDTO> CarregarUsuario(Email nomeDeUsuario);
        Task InserirUsuario(Usuario usuario);
        Task AtivarUsuario(Email nomeDeUsuario);
        Task InativarUsuario(Email nomeDeUsuario);
    }
}

And their due implementations in an Infrastructure layer (I’m not creating an infrastructure for each context).

If I didn’t care about class separation in contexts, I could simply dictate that my Uow interface had a get for Iusuariorepository, but in this scenario, it would cause a circular dependency, since my "Security" context also depends on the "Shared Kernel".

Seeing another scenario, creating an infrastructure and an API for each context, this problem would also be avoided as it could specialize my Uow for each Infra, but I believe that this would lead to a more complex path than I wish to address at the moment (Micro services?).

I always see that Service Locator is an anti-Pattern, but at the moment, I can’t see a solution without this approach.

1 answer

0


Issues involving Patterns and architectures hardly have a single correct answer and the discussion can easily become opinionated, so what we should do is understand the context of the implementation and arrive at a scalable solution that meets the requirements that have.

This gains even more emphasis when talking about Unit Of Work Pattern, because the very example of microsoft implementation is different to the UML proposed by Martin Fowler who designed the Pattern. Microsoft itself states in the link above, that there are several different implementations, and not necessarily incorrect, for this Pattern.

Analyzing the structure of your question, it is clear that your current implementation is following the Microsoft model, where the class Unit Of Work is responsible for storing and providing the repositories she works with.

If you want to follow this format, do not recommend using Service Locator to return the repositories, not because it is considered an anti-pattern, but because of the lack of coherence: The goal of storing the repositories in the state of the class of UnitOfWork is to segregate the repositories whose operations, will use the Context of that Unitofwork. When using Service Locator for that responsibility, we have no more control on which repositories that UoW is managing.

Obviously this situation could be circumvented by creating containers de DI with the dependencies of the isolated repositories, or solving all the Repositórios at once, and adding them to an internal list in the class UoW. But it is questionable the gain of this approach, after all, although we are solving the problem of incorrect use, we are still leaving the class without clear indications of what it has, and it is always necessary to check its implementation to know which repositories it holds.

If the goal is to follow this path, I find preferable the structure proposed in the microsoft tutorial:

Example:

interface IUnitOfWork
{
    public IUsuarioRepository UsuarioRepository { get; }
    // outros repositórios
    public void Save();
}

In this format, you will have to create a new interface for each different DbContext, as ISegurancaUnitOfWork , IProdutosUnitOfWork. But if in any way you are using a camada de Infraestrutura shared, the benefit of coherence is greater than the few lines of code that would be spared.

Hope I helped, hugs!

  • many thanks for the cordiality and the indication, I will follow in the studies!

Browser other questions tagged

You are not signed in. Login or sign up in order to post.