CQRS - apa yang harus dilakukan dengan kode yang perlu digunakan di banyak penangan sekaligus?







Saat menggunakan arsitektur dalam gaya irisan vertikal, cepat atau lambat timbul pertanyaan "apa yang harus dilakukan jika kode muncul yang perlu digunakan di beberapa penangan sekaligus?"







TLDR: Anda perlu membuat middleware penangan dan menambahkan antarmuka penanda khusus untuk memperjelas penangan mana yang merupakan abstraksi holistik dan mana yang bukan.

Jawaban atas pertanyaan ini tidak selalu jelas. Jimmy Boggard, misalnya, menyarankan " refactoring saja ". Saya sepenuhnya mendukung pendekatan ini, tetapi bentuk jawabannya menurut saya sama mengecewakannya dengan proposal untuk menggunakan monad gratis untuk injeksi ketergantungan dalam pemrograman fungsional . Nasihat ini tepat dan singkat, tetapi tidak terlalu membantu. Saya akan mencoba menjawab pertanyaan ini lebih detail.







Refactoring



Jadi, saya akan menggunakan dua teknik refactoring:







  1. Metode ekstrak
  2. Ekstrak kelas


, :







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. Memasukkan ketergantungan lapisan domain sebagai argumen konstruktor


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