DEV Community

Cover image for Entendendo o Pipeline do Brighter
Rafael Andrade
Rafael Andrade

Posted on

Entendendo o Pipeline do Brighter

O Brighter adota uma abordagem distinta em comparação a muitos outros frameworks, priorizando a clareza explícita em seu pipeline de tratamento de requisições. Em vez de depender de convenções ocultas ou configurações complexas, você define explicitamente o comportamento do pipeline usando atributos, garantindo controle total sobre a ordem de execução.

O Modelo da Boneca Russa (Matryoshka)

O pipeline do Brighter é arquitetado com base no Modelo da Boneca Russa. Imagine um conjunto de bonecas encaixadas: cada boneca contém uma menor em seu interior.

No Brighter, cada componente de middleware (uma "boneca") envolve o próximo na cadeia. Um método handler fica no centro. Quando uma requisição é processada, ela atravessa cada camada de middleware antes de chegar ao handler, e depois retorna passando novamente por cada camada na ordem inversa. Esse design implementa perfeitamente o Padrão Pipe and Filter.

Um recurso essencial desse modelo é a capacidade de interromper o pipeline (short-circuit). Qualquer middleware pode parar o processamento e retornar um resultado imediatamente, impedindo que a requisição chegue às camadas internas (incluindo o próprio handler).

Essa arquitetura permite que qualquer etapa do pipeline:

  1. Execute ações antes da requisição chegar ao handler.
  2. Encaminhe a requisição para a próxima etapa.
  3. Execute ações após o retorno do handler interno.
  4. Interrompa o pipeline retornando antecipadamente (cancelando o processamento).

Observação: Embora este guia use exemplos de handlers síncronos, os mesmos conceitos se aplicam a handlers assíncronos. Basta usar os atributos e classes base correspondentes (ex: RequestHandlerAsync<T>).

Adicionando um Pipeline ao Seu Handler

No Brighter, middlewares são adicionados via Atributos do C#. Você deve decorar explicitamente o método Handle do seu Request Handler.

A ordem de execução é controlada pelo parâmetro step:

  • Números menores executam primeiro na entrada e por último na saída.
public class SampleHandler : RequestHandler<SomeMessage> { // O 'step' define a ordem.  // Step 1 executa antes do Step 2. [RequestLogging(step: 0)] public virtual TRequest Handle(TRequest command) { ... } } 
Enter fullscreen mode Exit fullscreen mode

Implementando Middleware "Global"

O Brighter não oferece uma API fluente (ex: services.AddGlobalMiddleware(...)) para injetar lógica globalmente em todas as requisições. Essa é uma escolha deliberada de design para manter o pipeline explícito no nível do handler.

Porém, para aplicar lógica a todos os handlers ("middleware global"), use herança: crie um Handler Base com os atributos desejados. Todos os handlers que herdarem dessa classe adotarão o pipeline definido.

Exemplo: Criando um Handler Base com Políticas Globais

// 1. Crie um handler base com middleware comum public abstract class MyGlobalPolicyHandler<T> : RequestHandler<T> where T : class, IRequest { [RequestLogging(step: 0)] [FallbackPolicy(backstop: true, circuitBreaker: false, step: 1)] public override T Handle(T command) { // Este método propaga o comando pelo pipeline até o handler real. return base.Handle(command); } } // 2. Herde do handler base nas suas implementações public class SampleHandler : MyGlobalPolicyHandler<SomeCommand> { [UseResiliencePipeline("MyPolicy", step: 2)] public override SomeCommand Handle(SomeCommand command) { // Sua lógica de negócio principal aqui. Console.WriteLine("Handling SomeCommand"); return base.Handle(command); } } 
Enter fullscreen mode Exit fullscreen mode

Ordem de execução para SampleHandler:

RequestLoggingFallbackPolicyUseResiliencePipelineSampleHandler.Handle.

Middleware Oferecido pelo Brighter

O Brighter inclui middlewares prontos para uso:

  • RequestLoggingAttribute / RequestLoggingAsyncAttribute → Registra a entrada/saída do pipeline, incluindo tempo de execução e detalhes da requisição.
  • FallbackPolicyAttribute / FallbackPolicyAsyncAttribute → Define uma ação de fallback se a execução falhar. Chama o método Fallback do handler.
  • TimeoutPolicyAttribute → (Obsoleto) Define tempo máximo de execução. Prefira UseResiliencePipeline.
  • UsePolicyAttribute / UsePolicyAsyncAttribute → (Obsoleto) Usa políticas do Polly. Prefira UseResiliencePipeline.
  • UseResiliencePipelineAsyncAttribute / UseResiliencePipelineAttribute → Aplica pipelines de resiliência do Polly (retry, circuit-breaker, timeouts).
  • MonitorAttribute / MonitorAsyncAttribute → Permite monitoramento de handlers.
  • FeatureSwitchAttribute / FeatureSwitchAsyncAttribute → Habilita/desabilita dinamicamente um handler com base em um feature flag.

Implementando Seu Próprio Middleware

Para criar middleware personalizado, você precisa de duas classes por modo de execução (síncrono/assíncrono):

  1. O Atributo: Decora o método do handler.
  2. O Handler: Contém a lógica do middleware.

Exemplo de Implementação:

// 1. Classe do Atributo [AttributeUsage(AttributeTargets.Method)] public class MyFeatureFlagAttribute : RequestHandlerAttribute { public string FeatureName { get; } public MyFeatureFlagAttribute(string featureName, int step) : base(step, HandlerTiming.Before) { FeatureName = featureName; } public override object[] InitializerParams() { return new object[] { FeatureName }; } public override Type GetHandlerType() { // Indica qual classe handler usar para este atributo. return typeof(MyFeatureFlagHandler<>); } } // 2. Classe do Handler public class MyFeatureFlagHandler<TRequest> : RequestHandler<TRequest> where TRequest : class, IRequest { private string _featureName; private IFeatureManager _featureManager; // Gerenciador de features hipotético public override void InitializeFromAttributeParams(params object[] initializerList) { // Recebe parâmetros do método InitializerParams() _featureName = (string)initializerList[0]; _featureManager = ...; // Normalmente injetado via DI (Injeção de Dependência) } public override TRequest Handle(TRequest command) { // Verifica se a funcionalidade está habilitada if (!_featureManager.IsEnabled(_featureName)) { return command; } // Se habilitada, continua para o próximo handler no pipeline. return base.Handle(command); } } // 3. Usando o Middleware Personalizado public class DiscountHandler : RequestHandler<ApplyDiscountCommand> { [MyFeatureFlag("AdvancedDiscounts", step: 1)] public override ApplyDiscountCommand Handle(ApplyDiscountCommand command) { // Este código só executa se a funcionalidade "AdvancedDiscounts" estiver habilitada. return base.Handle(command); } } 
Enter fullscreen mode Exit fullscreen mode

Conclusão

O pipeline explícito e baseado em atributos do Brighter oferece clareza e controle incomparáveis sobre as preocupações transversais da sua aplicação. Ao utilizar o Modelo da Boneca Russa, você constrói pipelines de handlers robustos, bem ordenados e fáceis de manter. Seja com políticas embutidas ou middlewares personalizados, o processo permanece consistente e transparente.

Referência

Documentação Oficial: Building a Pipeline of Request Handlers

Top comments (0)