Definindo Casos de Uso da Aplicação
Nesta seção, exploraremos como definir os casos de uso da sua aplicação utilizando o Lino CLI. Casos de uso representam interações especĂficas entre usuários ou sistemas externos e a aplicação, encapsulando regras de negĂłcio e promovendo uma arquitetura orientada ao domĂnio. Abordaremos a separação entre operações de leitura e escrita por meio do padrĂŁo CQRS, alĂ©m de padronizar o retorno de respostas utilizando o Result Pattern.
O padrão CQRS (Command Query Responsibility Segregation) propõe a separação das operações que modificam o estado da aplicação (Commands) daquelas que apenas consultam dados (Queries). Essa abordagem permite otimizar cada tipo de operação de forma independente, melhorando a escalabilidade, a performance e a manutenção do sistema.
O Result Pattern é utilizado para padronizar o retorno de operações, encapsulando informações sobre sucesso ou falha, mensagens de erro e dados resultantes. Isso facilita o tratamento consistente de respostas em diferentes camadas da aplicação.
Veremos como sĂŁo estruturados os arquivos de Commands e Queries, as validações de dados, como sĂŁo aplicadas as lĂłgicas de domĂnio nos Handlers e como retornar resultados de forma padronizada. Tudo isso atravĂ©s da criação automática desses componentes por meio do CLI do Lino.
Nota: Embora não seja obrigatório, o Lino atualmente oferece duas opções para aplicar o padrão CQRS na camada de aplicação: utilizando o padrão Mediator por meio da biblioteca MediatR
(de Jimmy Bogard) ou da biblioteca Mediator
(de Martin Othamar).
VisĂŁo Geral de Use Cases
Um Use Case representa uma interação completa entre usuários ou sistemas externos e a aplicação, descrevendo cenários de negĂłcio especĂficos. No Lino, cada Use Case Ă© dividido em:
- Command: representa a intenção de modificar o estado do sistema (criar, atualizar, excluir, etc.).
- Query: representa a intenção de consultar dados sem alterar o estado do domĂnio.
Essa separação favorece a clareza do cĂłdigo, facilita testes, permite escalabilidade independente para leitura e escrita, e alinha-se a princĂpios de arquitetura limpos e boas práticas de Domain-Driven Design (DDD).
Commands
Um Command é uma mensagem imutável que carrega apenas os dados necessários para executar uma ação que modifica o estado do sistema (por exemplo, CreateInvoice
, DeactivateUser
).
Ele deve conter apenas as propriedades que representam as informações essenciais para realizar a operação.
CaracterĂsticas de um Command
- Imutabilidade: implementado como um
record
ou classe apenas comget
, sem setters públicos. - Nome no imperativo: reflete a ação que será executada, por exemplo,
CreateOrder
,UpdateCustomerAddress
. - Dados mĂnimos: contĂ©m somente os campos necessários para executar a operação, sem retornar grandes volumes de dados.
- Validação isolada: cada Command possui regras de validação próprias, garantindo que esteja consistente antes de chegar ao manipulador (Handler).
Command Validators
Os Command Validators garantem que o Command esteja bem-formado e atendendo aos requisitos de negócio antes de ser enviado ao Handler. No Lino, utilizamos a biblioteca FluentValidation para implementar essas validações, pois é amplamente adotada em projetos .NET e oferece Fluent API para criação de regras.
Regras Comuns de Validação
NotEmpty
,NotNull
para campos obrigatĂłrios.InclusiveBetween
para faixas numĂ©ricas (ex.: valores monetários, quantidade mĂnima/máxima).MaximumLength
eMinimumLength
para tamanho de strings.RuleForEach
para validação de itens em coleções (List<T>
).Must
para regras customizadas (ex.: formato de documentos, validação de data).
Command Handlers
O Command Handler Ă© responsável por executar a lĂłgica de domĂnio associada Ă quele Command. Ele orquestra repositĂłrios, unidades de trabalho (IUnitOfWork
), serviços auxiliares e dispara eventos de domĂnio quando necessário.
Padrão de Implementação de um Handler
- Receber dependências (repositórios, serviços externos, UnitOfWork) por injeção de dependência.
- Mapear e instanciar entidades do domĂnio, garantindo a consistĂŞncia inicial.
- Aplicar regras de negĂłcio (validações adicionais, cálculo de valores, disparo de eventos de domĂnio).
- Persistir alterações por meio de
IUnitOfWork
ou repositĂłrios diretos. - Retornar um Command Result, que indica sucesso ou fracasso (
Result<T>
).
Command Results e Result Pattern
O Command Result deve ser um DTO simples contendo apenas os dados mĂnimos necessários para que o caller (por exemplo, uma API ou front-end) prossiga. Geralmente, inclui o identificador da entidade criada ou atualizada (Id
) e, se houver falha, informações padronizadas sobre o erro.
Para cenários onde o Command pode falhar, utilizamos o Result Pattern: uma abstração que encapsula o resultado de uma operação, podendo ser sucesso ou falha. Em .NET, é comum usar tipos como Result<T>
(ou bibliotecas similares), que:
- Permitem definir um
Value
em caso de sucesso (por exemplo, um DTO simples). - Em caso de falha, armazenam cĂłdigos ou mensagens padronizadas (
Error
), possivelmente com metadados extras (códigos HTTP, detalhes de validação, etc.). - Facilitam a unificação do fluxo de tratamento de erros em toda a API, mantendo consistência entre camadas.
Criando um Command com o CLI
O Lino simplifica a geração de todos os artefatos necessários para um novo Command por meio do comando:
lino command new
Ao executar esse comando, o assistente interativo solicitará:
- Serviço — Nome do serviço onde o Command será criado.
- Módulo — Em serviços modularizados, define o módulo de destino.
- Entidade — Entidade de domĂnio relacionada ao Command (opcional, mas recomendado).
- Nome do Command — Nome final do Command (por exemplo,
CreateOrder
).
Após confirmar, o Lino criará automaticamente os arquivos:
CreateOrderCommand.cs
CreateOrderCommandValidator.cs
CreateOrderCommandHandler.cs
CreateOrderCommandResult.cs
Exemplo de Estrutura Gerada
Considere o Command CreatePerson
. A estrutura gerada será semelhante a:
MyApp/ └── src/ └── Services/ └── MyService/ └── Application/ ├── MyApp.MyService.Application.csproj └── UseCases/ └── People/ ├── Commands/ │ └── CreatePerson/ │ ├── CreatePersonCommand.cs │ ├── CreatePersonCommandValidator.cs │ ├── CreatePersonCommandHandler.cs │ └── CreatePersonCommandResult.cs └── Queries/ └── ...
Queries
Uma Query representa a intenção de obter dados sem alterar o estado do domĂnio. As Queries sĂŁo projetadas para ser eficientes em leitura, retornando exatamente os campos necessários, sem carregar entidades inteiras quando nĂŁo requisitado.
CaracterĂsticas de uma Query
- Imutável — semelhante aos Commands, a Query não deve permitir alterações após a criação.
- Nome descritivo — reflete a informação buscada, por exemplo,
GetCustomerById
,ListOrdersByDateRange
. - Filtros e Paginação — pode carregar parâmetros para aplicar buscas especĂficas (datas, status, página, ordenação).
- Projeção — deve retornar um DTO ou view model com apenas os campos necessários, evitando carregamento de objetos de domĂnio completos.
Query Validators
Os Query Validators têm a responsabilidade de validar parâmetros de entrada, como filtros, valores de paginação (page, pageSize) e regras de segurança (permissões de usuário, visibilidade de dados). Também são implementados com FluentValidation.
Regras Comuns de Validação em Queries
GreaterThanOrEqualTo
ouLessThanOrEqualTo
para filtros de intervalo (ex.: datas).Length
para filtros de texto (ex.: busca por nome, e-mail).InclusiveBetween
para parâmetros de paginação (ex.: nĂşmero mĂnimo/máximo de itens por página).
Query Handlers
O Query Handler Ă© responsável por consultar os repositĂłrios ou o contexto do banco de dados e retornar projeções otimizadas, evitando o carregamento completo das entidades de domĂnio.
No Lino, recomenda-se o uso de projeções com métodos como .Select()
para mapear diretamente os dados no formato esperado de DTO, garantindo eficiĂŞncia e melhor desempenho.
Query Results
No Lino, os resultados de queries sĂŁo sempre representados por records nomeados com o sufixo QueryResult
.
Essa convenção se aplica independentemente do tipo de resposta — seja uma lista de itens (com ou sem paginação) ou um único item detalhado.
Criando uma Query com o CLI
Similar aos Commands, o Lino disponibiliza o comando:
lino query new
O assistente solicitará:
- Serviço — Serviço onde a Query será criada.
- MĂłdulo — Quando aplicável, define o mĂłdulo de domĂnio.
- Entidade — Entidade ou agregado ao qual a Query está associada (opcional).
- Nome da Query — Por exemplo,
GetOrderById
ouListProductsByCategory
.
O Lino gerará automaticamente:
GetOrderByIdQuery.cs
GetOrderByIdQueryValidator.cs
GetOrderByIdQueryHandler.cs
GetOrderByIdQueryResult.cs
Exemplo de Estrutura Gerada para Queries
MyApp/ └── src/ └── Services/ └── MyService/ └── Application/ ├── MyApp.MyService.Application.csproj └── UseCases/ └── Orders/ ├── Commands/ | └── ... └── Queries/ └── GetOrderById/ ├── GetOrderByIdQuery.cs ├── GetOrderByIdQueryValidator.cs ├── GetOrderByIdQueryHandler.cs └── GetOrderByIdQueryResult.cs
ConclusĂŁo
ApĂłs entender como definir e estruturar Use Cases (Commands e Queries) no Lino, vocĂŞ está preparado para criar cenários de negĂłcio robustos e escaláveis, segmentando claramente a lĂłgica de escrita e leitura. Use o CLI para acelerar o desenvolvimento, mas sempre revise e ajuste as implementações conforme necessidades especĂficas do seu domĂnio.
Em seguida, explore como lidar com PersistĂŞncia, Eventos de DomĂnio e Serviços de Infraestrutura para compor toda a arquitetura da sua aplicação.