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)