Camada de Aplicação
Arquitetura Limpa
Criação do Projeto de Aplicação
Um novo projeto do tipo biblioteca de classes é criado para representar a camada de aplicação na estrutura da Arquitetura Limpa:
dotnet new classlib -o CleanArchitectureStore.Application
dotnet sln add .\CleanArchitectureStore.Application\O projeto CleanArchitectureStore.Application é adicionado à solução, estabelecendo a camada responsável por orquestrar os fluxos de aplicação, implementar casos de uso e mediar a comunicação entre a apresentação e o domínio.
Princípios CQS e CQRS
Segregação de Comando e Consulta (CQS)
O Princípio de Segregação de Comando e Consulta (Command Query Separation - CQS), formulado por Bertrand Meyer, estabelece que:
Comandos (Commands): Realizam ações, alteram estado, não retornam valores
Consultas (Queries): Retornam resultados, não alteram estado, são idempotentes
Segregação de Responsabilidade de Comando e Consulta (CQRS)
CQRS (Command Query Responsibility Segregation) estende o CQS ao utilizar modelos separados para operações de leitura e escrita:
Modelo de Comando: Otimizado para operações de escrita, com validação de negócio
Modelo de Consulta: Otimizado para operações de leitura, com projeções eficientes
Na implementação atual, adota-se uma abordagem moderada do CQS, onde as responsabilidades são separadas conceitualmente, mantendo a mesma fonte de dados.
Implementação com Padrão Mediator
Instalação e Configuração do MediatR
O padrão Mediator é implementado através do pacote MediatR:
O MediatR atua como mediador entre os comandos e seus manipuladores, desacoplando a invocação da execução. Sua função principal é rotear automaticamente um comando para o handler apropriado, sem necessidade de acoplamento direto.
Estrutura do Caso de Uso
O caso de uso GetById é organizado seguindo o padrão:
Implementação do Result Pattern
Classe de Erro (Error)
No projeto de domínio, é criada uma estrutura para representação padronizada de erros:
Características do record Error:
Imutabilidade:
recordgarante que instâncias não podem ser modificadas após criaçãoIgualdade por Valor: Comparação automática baseada no conteúdo, não na referência
Códigos Semânticos: Identificadores padronizados para tipos de erro
Mensagens Descritivas: Texto legível para diagnóstico
Classe Base Result
Uma implementação do padrão Result é criada para substituir o uso de valores nulos e exceções de controle de fluxo:
Análise da classe base Result:
Validação de Invariantes: O construtor valida que resultados bem-sucedidos não têm erros e vice-versa
Propriedades de Estado:
IsSuccesseIsFailurefornecem verificação simples do estadoMétodos de Fábrica: Métodos estáticos para criação consistente de resultados
Tipagem Segura: Separação entre resultados com e sem valor
Classe Genérica Result
Funcionalidades do Result<T>:
Encapsulamento de Valor: Armazena o valor retornado em operações bem-sucedidas
Acesso Seguro: A propriedade
Valuelança exceção se acessada em resultados falhosConversão Implícita: Permite uso natural como
return someValue;em vez dereturn Result.Success(someValue);
Definição do Command
O comando é implementado como um record selado, representando a entrada para o caso de uso:
Características do Command:
Imutabilidade:
recordgarante que o comando não pode ser alterado após criaçãoSelado: Prevenção de herança não intencional
Implementação de
IRequest<T>: Define o tipo de resposta esperado pelo MediatRParâmetros Nomeados:
Guid Ididentifica claramente o propósito do parâmetro
Definição do Response
A resposta é também implementada como um record selado:
Propriedades do Response:
Estrutura Simples: Apenas os dados necessários para o consumidor
Separação de Modelos: Diferente da entidade de domínio, pode incluir ou excluir propriedades conforme necessidade
Performance: Records têm otimizações para comparação e cópia
Implementação do Handler
O manipulador (handler) é responsável pela orquestração da lógica do caso de uso:
Análise do Handler
Injeção de Dependências:
IProductRepository: Interface definida no domínio, implementada na infraestruturaO construtor primário do C# 12 simplifica a declaração de dependências
Processamento:
Busca Assíncrona: Utilização de
awaitpara operações não-bloqueantesTratamento de CancellationToken: Respeito ao cancelamento cooperativo
Validação de Resultado: Verificação explícita de
nullRetorno Tipado: Utilização do padrão
Result<T>para comunicação de sucesso/falha
Padrões de Mapeamento: Embora mencionado o uso de mapeadores automáticos como AutoMapper ou Mapster, a implementação atual utiliza mapeamento manual por:
Simplicidade: Para casos simples, o mapeamento manual é mais claro
Performance: Eliminação de overhead de reflexão
Controle: Visibilidade completa da transformação
Alternativas de mapeamento mencionadas:
Cast Operator: Sobrecarga de operadores de conversão
Extension Methods: Métodos de extensão para conversão
Métodos de Fábrica: Métodos estáticos na classe de resposta
Referência ao Projeto de Domínio
O projeto de aplicação referencia o domínio para acessar as definições de interfaces e padrões:
Esta referência estabelece a direção correta de dependência na Arquitetura Limpa: a camada de aplicação depende do domínio, nunca o contrário.
Benefícios da Abordagem
Desacoplamento Arquitetural
Separação de Responsabilidades: Cada componente tem uma única responsabilidade bem definida
Independência de Framework: O domínio permanece livre de dependências externas
Testabilidade Isolada: Cada componente pode ser testado independentemente
Manutenibilidade
Localização de Mudanças: Alterações em regras de negócio são confinadas ao domínio
Evolução Independente: A API pode evoluir sem impactar a lógica de negócio
Reutilização: Casos de uso podem ser consumidos por diferentes apresentações (Web API, Console, etc.)
Robustez
Tratamento Explícito de Erros: O padrão
Resultelimina exceções para fluxos de negócio esperadosTipagem Forte: Compilador detecta incompatibilidades em tempo de compilação
Imutabilidade:
recorde padrões funcionais reduzem efeitos colaterais
Considerações de Implementação
Escolha de Records
A utilização de record oferece:
Igualdade por Valor: Comparação baseada em conteúdo, não referência
Imutabilidade Nativa: Propriedades são
init-onlypor padrãoSintaxe Concisa: Declaração simplificada com parâmetros posicionais
Performance: Otimizações para hash code e igualdade
Padrão Result vs Exceções
O padrão Result é preferido sobre exceções para:
Erros Esperados: Fluxos alternativos de negócio (como "recurso não encontrado")
Performance: Exceções são computacionalmente caras para fluxos normais
Clareza: O tipo de retorno declara explicitamente possíveis falhas
Composição: Facilidade em combinar múltiplas operações com resultados
MediatR como Implementação do Padrão Mediator
Vantagens do MediatR:
Desacoplamento Total: Emissores não conhecem receptores
Pipeline Behaviors: Middleware para cross-cutting concerns (logging, validação, etc.)
Pub/Sub: Suporte a notificações para múltiplos handlers
Simplicidade: Configuração mínima necessária
Próximas Etapas
Com o caso de uso implementado, as próximas ações incluem:
Implementação de Infraestrutura: Repositórios concretos com Entity Framework
Configuração de API: Endpoints que consomem os casos de uso
Validação: Pipeline behaviors para validação de commands
Logging e Monitoring: Cross-cutting concerns através de behaviors
Testes: Testes unitários para handlers e de integração para fluxos completos
Esta estrutura estabelece uma base sólida para desenvolvimento de aplicações complexas, onde a clareza, manutenibilidade e testabilidade são priorizadas através da aplicação consistente dos princípios da Arquitetura Limpa.
Atualizado