CQRS - what to do with code that needs to be used in multiple handlers at once?







When using architecture in the style of vertical slices, sooner or later the question arises "what to do if code appears that needs to be used in several handlers at once?"







TLDR: We need to create a handler middleware and add custom marker interfaces to make it clear which handlers are holistic abstractions and which are not.

The answer to this question is not always obvious. Jimmy Boggard, for example, suggests " just refactoring ." I fully support this approach, but the form of the answer seems to me as discouraging as the proposal to use a free monad for dependency injection in functional programming . This advice is precise and short, but not very helpful. I will try to answer this question in more detail.







Refactoring



So, I will use two refactoring techniques:







  1. Extract method
  2. Extract class


, :







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. Injecting the domain layer dependency as constructor arguments


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