CQRS - que faire du code qui doit être utilisé dans plusieurs gestionnaires à la fois?







Lors de l'utilisation de l'architecture dans le style des tranches verticales, la question se pose tôt ou tard "que faire si du code apparaît et doit être utilisé dans plusieurs gestionnaires à la fois?"







TLDR: Vous devez créer un middleware de gestionnaire et ajouter des interfaces de marqueurs personnalisées pour indiquer clairement quels gestionnaires sont des abstractions holistiques et lesquels ne le sont pas.

La réponse à cette question n'est pas toujours évidente. Jimmy Boggard, par exemple, suggère « juste refactoring ». Je soutiens pleinement cette approche, mais la forme de la réponse me semble aussi décourageante que la proposition d' utiliser une monade gratuite pour l'injection de dépendances dans la programmation fonctionnelle . Ce conseil est précis et court, mais pas très utile. Je vais essayer de répondre à cette question plus en détail.







Refactoring



Donc, je vais utiliser deux techniques de refactoring:







  1. Méthode d'extraction
  2. Extraire la 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. Injection de la dépendance de couche de domaine en tant qu'arguments du constructeur


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