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:
- Execute ações antes da requisição chegar ao handler.
- Encaminhe a requisição para a próxima etapa.
- Execute ações após o retorno do handler interno.
- 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)
{
...
}
}
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);
}
}
Ordem de execução para SampleHandler:
RequestLogging → FallbackPolicy → UseResiliencePipeline → SampleHandler.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étodoFallbackdo handler. -
TimeoutPolicyAttribute→ (Obsoleto) Define tempo máximo de execução. PrefiraUseResiliencePipeline. -
UsePolicyAttribute/UsePolicyAsyncAttribute→ (Obsoleto) Usa políticas do Polly. PrefiraUseResiliencePipeline. -
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):
- O Atributo: Decora o método do handler.
- 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);
}
}
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)