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 com get, 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 e MinimumLength 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 ou LessThanOrEqualTo 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 ou ListProductsByCategory.

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.

Ocorreu um erro não tratado. Recarregar đź—™