CQRS - o que fazer com o código que precisa ser usado em vários manipuladores ao mesmo tempo?







Ao usar arquitetura no estilo de fatias verticais, mais cedo ou mais tarde surge a questão "o que fazer se aparecer um código que precise ser usado em vários manipuladores ao mesmo tempo?"







TLDR: você precisa criar um middleware de manipulador e adicionar interfaces de marcador personalizadas para deixar claro quais manipuladores são abstrações holísticas e quais não são.

A resposta a esta pergunta nem sempre é óbvia. Jimmy Boggard, por exemplo, sugere " apenas refatorar ". Eu apoio totalmente essa abordagem, mas a forma da resposta me parece tão desanimadora quanto a proposta de usar uma mônada livre para injeção de dependência na programação funcional . Este conselho é preciso e curto, mas não muito útil. Tentarei responder a essa pergunta com mais detalhes.







Reestruturação



Portanto, usarei duas técnicas de refatoração:







  1. Método de extração
  2. Extrair classe


, :







public IEnumerable<SomeDto> Handle(SomeQuery q)
{
    // 100  ,
    //     

    // 50  ,   
    //   

    return result;
}
      
      





, , 100 50 . , . «», ctrl+shift+r -> extract method . — .

, , - :







public IEnumerable<SomeDto> Handle(SomeQuery q)
{
    var shared = GetShared(q);
    var result = GetResult(shared);
    return result;
}
      
      





?



: ? . , :







public IEnumerable<SomeDto> Handle(SomeQuery q)
{
    var shared1 = GetShared1(q);
    var shared2 = GetShared2(q);
    var shared3 = GetShared3(q);
    var shared4 = GetShared4(q);

    var result = GetResult(shared1,shared2, shared3, shared4);
    return result;
}
      
      





.



, .







public class ConcreteQueryHandler: 
    IQueryHandler<SomeQuery, IEnumerable<SomeDto>>
{
    ??? _sharedHandler;

    public ConcreteQueryHandler(??? sharedHandler)
    {
        _sharedHandler = sharedHandler;
    }
}
      
      







///- (Domain Services). IDomainHandler<TIn, TOut>



, IHandler<TIn, TOut>



.







. . , — , .







.







public class ConcreteQueryHandler2:
    IQueryHandler<SomeQuery,  IEnumerable<SomeDto>>
{
    IDomainHandler<???, ???> _sharedHandler;

    public ConcreteQueryHandlerI(IDomainHandler<???, ???> sharedHandler)
    {
        _sharedHandler = sharedHandler;
    }
}

public class ConcreteQueryHandler2:
    IQueryHandler<SomeQuery,  IEnumerable<SomeDto>>
{
    IDomainHandler<???, ???> _sharedHandler;

    public ConcreteQueryHandlerI(IDomainHandler<???, ???> sharedHandler)
    {
        _sharedHandler = sharedHandler;
    }
}
      
      





?



, IHandler



. , , .









, , . , « ». , .




-: , IDomainHandler<???, ???>



. :







  1. ICommand/IQuery



    ?
  2. IQueryable<T>



    ?


ICommand/IQuery



?



, :







public interface ICommand<TResult>
{
}

public interface IQuery<TResult>
{
}
      
      





IDomainHandler



Command/Query



, .







IQueryable<T>



?



, ORM:) … LINQ LSP , — «». , , . IQueryable



— .









  1. Injetando a dependência da camada de domínio como argumentos do construtor


public class ConcreteQueryHandler:
    IQueryHandler<SomeQuery,  IEnumerable<SomeDto>>
{
    IDomainHandler<
        SomeValueObjectAsParam,
        IQueryable<SomeDto>>_sharedHandler;

    public ConcreteQueryHandler(
        IDomainHandler<
            SomeValueObjectAsParam,
            IQueryable<SomeDto>>)
    {
        _sharedHandler = sharedHandler;
    }

    public IEnumerable<SomeDto> Handle(SomeQuery q)
    {
        var prm = new SomeValueObjectAsParam(q.Param1, q.Param2);
        var shared = _sharedHandler.Handle(prm);

        var result = shared
          .Where(x => x.IsRightForThisUseCase)
          .ProjectToType<SomeDto>()
          .ToList();

        return result;
    }
}
      
      






All Articles