Definiendo casos de uso de la aplicación

En esta sección, exploraremos cómo definir los casos de uso de tu aplicación utilizando Lino CLI. Los casos de uso representan interacciones específicas entre usuarios o sistemas externos y la aplicación, encapsulando reglas de negocio y promoviendo una arquitectura orientada al dominio. Abordaremos la separación entre operaciones de lectura y escritura mediante el patrón CQRS, además de estandarizar las respuestas utilizando el Result Pattern.


El patrón CQRS (Command Query Responsibility Segregation) propone la separación de las operaciones que modifican el estado de la aplicación (Commands) de aquellas que solo consultan datos (Queries). Este enfoque permite optimizar cada tipo de operación de forma independiente, mejorando la escalabilidad, el rendimiento y el mantenimiento del sistema.


El Result Pattern se utiliza para estandarizar el retorno de las operaciones, encapsulando información sobre el éxito o fallo, mensajes de error y datos resultantes. Esto facilita un manejo coherente de las respuestas en las distintas capas de la aplicación.


Veremos cómo están estructurados los archivos de Commands y Queries, las validaciones de datos, cómo se aplica la lógica de dominio en los Handlers y cómo devolver resultados de forma estandarizada. Todo esto a través de la creación automática de estos componentes por medio del CLI de Lino.

Nota: Aunque no es obligatorio, actualmente Lino ofrece dos opciones para aplicar el patrón CQRS en la capa de aplicación: utilizando el patrón Mediator mediante la biblioteca MediatR (de Jimmy Bogard) o la biblioteca Mediator (de Martin Othamar).

Visión General de los Casos de Uso

Un caso de uso representa una interacción completa entre usuarios o sistemas externos y la aplicación, describiendo escenarios de negocio específicos. En Lino, cada caso de uso se divide en:

  • Command: representa la intención de modificar el estado del sistema (crear, actualizar, eliminar, etc.).
  • Query: representa la intención de consultar datos sin alterar el estado del dominio.

Esta separación favorece la claridad del código, facilita las pruebas, permite una escalabilidad independiente para lectura y escritura, y se alinea con los principios de arquitectura limpia y las buenas prácticas de Domain-Driven Design (DDD).

Comandos

Un Comando es un mensaje inmutable que contiene solo los datos necesarios para ejecutar una acción que modifica el estado del sistema (por ejemplo, CreateInvoice, DeactivateUser). Debe contener únicamente las propiedades que representan la información esencial para realizar la operación.

Características de un Comando

  • Inmutabilidad: implementado como un record o clase con solo get, sin setters públicos.
  • Nombre en imperativo: refleja la acción que se ejecutará, por ejemplo, CreateOrder, UpdateCustomerAddress.
  • Datos mínimos: contiene solo los campos necesarios para ejecutar la operación, sin devolver grandes volúmenes de datos.
  • Validación aislada: cada Comando tiene sus propias reglas de validación, asegurando que esté consistente antes de llegar al manejador (Handler).

Validadores de Comando

Los Validadores de Comando aseguran que el Comando esté bien formado y cumpla con los requisitos del negocio antes de ser enviado al manejador. En Lino, usamos la biblioteca FluentValidation para implementar estas validaciones, ya que es ampliamente adoptada en proyectos .NET y ofrece una API Fluent para la creación de reglas.

Reglas comunes de validación

  • NotEmpty, NotNull para campos obligatorios.
  • InclusiveBetween para rangos numéricos (ej.: valores monetarios, cantidades mínimas/máximas).
  • MaximumLength y MinimumLength para la longitud de cadenas.
  • RuleForEach para validación de elementos en colecciones (List<T>).
  • Must para reglas personalizadas (ej.: formato de documentos, validación de fechas).

Manejadores de Comando

El Manejador de Comando es responsable de ejecutar la lógica de dominio asociada a ese Comando. Orquesta repositorios, unidades de trabajo (IUnitOfWork), servicios auxiliares y dispara eventos de dominio cuando es necesario.

Patrón de implementación de un manejador

  • Recibir dependencias (repositorios, servicios externos, UnitOfWork) mediante inyección de dependencias.
  • Mapear e instanciar entidades del dominio, garantizando la consistencia inicial.
  • Aplicar reglas de negocio (validaciones adicionales, cálculo de valores, disparo de eventos de dominio).
  • Persistir cambios mediante IUnitOfWork o repositorios directos.
  • Retornar un Resultado de Comando, que indica éxito o fracaso (Result<T>).

Resultados de Comando y Result Pattern

El Resultado de Comando debe ser un DTO simple que contenga solo los datos mínimos necesarios para que el llamador (por ejemplo, una API o front-end) continúe. Generalmente incluye el identificador de la entidad creada o actualizada (Id) y, si hay falla, información estandarizada sobre el error.

Para escenarios donde el Comando puede fallar, usamos el Result Pattern: una abstracción que encapsula el resultado de una operación, pudiendo ser éxito o fallo. En .NET, es común usar tipos como Result<T> (o bibliotecas similares), que:

  • Permiten definir un Value en caso de éxito (por ejemplo, un DTO simple).
  • En caso de fallo, almacenan códigos o mensajes estandarizados (Error), posiblemente con metadatos extras (códigos HTTP, detalles de validación, etc.).
  • Facilitan la unificación del flujo de manejo de errores en toda la API, manteniendo consistencia entre capas.

Creando un Comando con el CLI

Lino simplifica la generación de todos los artefactos necesarios para un nuevo Comando mediante el comando:

lino command new

Al ejecutar este comando, el asistente interactivo solicitará:

  • Servicio — Nombre del servicio donde se creará el Comando.
  • Módulo — En servicios modularizados, define el módulo destino.
  • Entidad — Entidad de dominio relacionada con el Comando (opcional, pero recomendado).
  • Nombre del Comando — Nombre final del Comando (por ejemplo, CreateOrder).

Tras confirmar, Lino creará automáticamente los archivos:

  • CreateOrderCommand.cs
  • CreateOrderCommandValidator.cs
  • CreateOrderCommandHandler.cs
  • CreateOrderCommandResult.cs

Ejemplo de estructura generada

Considera el Comando CreatePerson. La estructura generada será similar a:

MyApp/
└── src/
    └── Services/
        └── MyService/
            └── Application/
                ├── MyApp.MyService.Application.csproj
                └── UseCases/
                    └── People/
                        ├── Commands/
                        │   └── CreatePerson/
                        │       ├── CreatePersonCommand.cs
                        │       ├── CreatePersonCommandValidator.cs
                        │       ├── CreatePersonCommandHandler.cs
                        │       └── CreatePersonCommandResult.cs
                        └── Queries/
                            └── ...

Consultas

Una Consulta representa la intención de obtener datos sin alterar el estado del dominio. Las Consultas están diseñadas para ser eficientes en la lectura, devolviendo exactamente los campos necesarios, sin cargar entidades completas cuando no se requiere.

Características de una Consulta

  • Inmutable — al igual que los Comandos, una Consulta no debe permitir modificaciones después de su creación.
  • Nombre descriptivo — refleja la información que se busca, por ejemplo: GetCustomerById, ListOrdersByDateRange.
  • Filtros y Paginación — puede incluir parámetros para búsquedas específicas (fechas, estado, página, ordenamiento).
  • Proyección — debe devolver un DTO o modelo de vista con solo los campos necesarios, evitando la carga completa de objetos de dominio.

Validadores de Consultas

Los Validadores de Consultas son responsables de validar los parámetros de entrada, como filtros, valores de paginación (page, pageSize) y reglas de seguridad (permisos de usuario, visibilidad de los datos). También se implementan con FluentValidation.

Reglas Comunes de Validación en Consultas

  • GreaterThanOrEqualTo o LessThanOrEqualTo para filtros de rango (ej.: fechas).
  • Length para filtros de texto (ej.: búsqueda por nombre o correo electrónico).
  • InclusiveBetween para parámetros de paginación (ej.: número mínimo/máximo de ítems por página).

Manejadores de Consultas

El Manejador de Consultas es responsable de consultar los repositorios o el contexto de la base de datos y devolver proyecciones optimizadas, evitando la carga completa de las entidades de dominio. En Lino, se recomienda el uso de proyecciones con métodos como .Select() para mapear directamente los datos al formato esperado del DTO, garantizando eficiencia y mejor rendimiento.

Resultados de Consultas

En Lino, los resultados de las consultas siempre se representan mediante registros nombrados con el sufijo QueryResult. Esta convención se aplica independientemente del tipo de respuesta — ya sea una lista de ítems (con o sin paginación) o un único ítem detallado.

Creación de una Consulta con el CLI

Al igual que con los Comandos, Lino proporciona el comando:

lino query new

El asistente solicitará:

  • Servicio — Servicio donde se creará la Consulta.
  • Módulo — Cuando sea aplicable, define el módulo de dominio.
  • Entidad — Entidad o agregado al cual está asociada la Consulta (opcional).
  • Nombre de la Consulta — Por ejemplo: GetOrderById o ListProductsByCategory.

Lino generará automáticamente:

  • GetOrderByIdQuery.cs
  • GetOrderByIdQueryValidator.cs
  • GetOrderByIdQueryHandler.cs
  • GetOrderByIdQueryResult.cs

Ejemplo de Estructura Generada para Consultas

MyApp/
└── src/
    └── Services/
        └── MyService/
            └── Application/
                ├── MyApp.MyService.Application.csproj
                └── UseCases/
                    └── Orders/
                        ├── Commands/
                        |   └── ...
                        └── Queries/
                            └── GetOrderById/
                                ├── GetOrderByIdQuery.cs
                                ├── GetOrderByIdQueryValidator.cs
                                ├── GetOrderByIdQueryHandler.cs
                                └── GetOrderByIdQueryResult.cs

Conclusión

Después de entender cómo definir y estructurar los Use Cases (Commands y Queries) en Lino, estás listo para crear escenarios de negocio robustos y escalables, segmentando claramente la lógica de escritura y lectura. Usa el CLI para acelerar el desarrollo, pero siempre revisa y ajusta las implementaciones según las necesidades específicas de tu dominio.

A continuación, explora cómo manejar la persistencia, los eventos de dominio y los servicios de infraestructura para componer toda la arquitectura de tu aplicación.

Se ha producido un error no controlado. Recargar 🗙