Estructurando el Proyecto

Lino fue desarrollado para simplificar la creación de proyectos escalables y modulares de manera eficiente. Ofrece una solución bien estructurada, con una clara separación de responsabilidades entre las capas, y está preparado para crecer a medida que evolucionen las necesidades de su proyecto.


Al crear un proyecto con Lino, generas una solución .NET organizada según las mejores prácticas de arquitectura y modularización, enfocada en el rendimiento, la escalabilidad y la facilidad de mantenimiento.

Creando un Nuevo Proyecto

El comando lino project facilita la creación de nuevos proyectos .NET de manera simple y eficiente. Con él, puedes configurar la estructura de tu proyecto, seleccionar las dependencias necesarias y definir configuraciones de idioma e infraestructura.

Para crear un nuevo proyecto, utiliza el siguiente comando:

lino project new

Durante la ejecución, la CLI solicitará la siguiente información:

  • Espacio de nombres del Proyecto: Define el espacio de nombres principal de la solución.
  • Nombre de Visualización: Nombre amigable que se muestra en las interfaces.
  • Lenguaje de Programación: Actualmente solo se admite C# (.NET).
  • Stack de Desarrollo: Actualmente .NET 9 con Aspire.
  • ¿Utilizar Analizadores de Código? Elige entre Sí o No.
  • ¿Utilizar Caché Distribuido? Elige entre Sí o No.
  • ¿Utilizar Comunicación Asíncrona? Elige entre Sí o No.
  • Idioma de los Datos: Define el idioma utilizado para los nombres de las entidades y otros datos informados en el sistema.
  • Idiomas Soportados por la Aplicación: Permite agregar soporte para hasta 10 idiomas para internacionalización (i18n).
  • Idioma Predeterminado: Define el idioma principal utilizado en APIs, mensajes de retorno, validaciones y en la interfaz de usuario.

Después de confirmar la información, Lino creará automáticamente la estructura del proyecto, como se muestra a continuación:

MyApp/
├── MyApp.sln
├── src/
│   ├── Aspire/
│   │   ├── AppHost/
│   │   │   └── MyApp.AppHost.csproj
│   │   └── ServiceDefaults/
│   │       └── MyApp.ServiceDefaults.csproj
│   └── Services/
│       └── Shared/
│           ├── API/
│           │   └── MyApp.Shared.API.csproj
│           ├── Application/
│           │   └── MyApp.Shared.Application.csproj
│           ├── Domain/
│           │   └── MyApp.Shared.Domain.csproj
│           ├── Infrastructure/
│           │   └── MyApp.Shared.Infrastructure.csproj
│           └── Infrastructure.Persistence/
│               └── MyApp.Shared.Infrastructure.Persistence.csproj
└── tests/
    

Analizadores de Código

Los analizadores de código estático son herramientas poderosas que ayudan a garantizar la calidad y consistencia del código durante el desarrollo. Lo hacen inspeccionando el código fuente sin necesidad de ejecución, detectando errores, problemas de estilo y otras inconsistencias.

Cuando eliges habilitar los analizadores de código durante la creación del proyecto, la CLI configurará automáticamente los siguientes paquetes para ti:

  • StyleCop.Analyzers: Realiza verificaciones de estilo de código, como formato de sangrías, espaciado y convenciones de nombres.
  • SonarAnalyzer.CSharp: Un conjunto de reglas de calidad de código y seguridad, que ayuda a detectar fallas comunes y vulnerabilidades potenciales en el código.
  • Roslynator.Analyzers: Ofrece una variedad de reglas de calidad y mejoras para el código C#, ayudando a identificar oportunidades de refactorización y optimización.

Ventajas de los Analizadores de Código:

  • Mejora de la Calidad: Ayudan a mantener el código limpio, legible y libre de problemas comunes.
  • Prevención de Errores: Detectan errores antes de que se ejecuten, permitiendo que el desarrollador corrija los problemas temprano en el proceso.
  • Estándarización: Aseguran que todos los desarrolladores sigan las mismas convenciones y reglas de estilo, mejorando la consistencia del código.
  • Refactorización Asistida: Facilitan la refactorización del código, proporcionando sugerencias de mejora.

Al activar los analizadores de código, tendrás una forma proactiva de mejorar la calidad de tu código y reducir el riesgo de fallos en el entorno de producción.

Caché Distribuido

El caché distribuido es una técnica utilizada para mejorar el rendimiento y escalabilidad de las aplicaciones, almacenando datos frecuentemente accedidos en una capa de caché externa a la base de datos. Permite que las instancias de la aplicación compartan datos en caché de manera eficiente, garantizando alta disponibilidad y reducción del tiempo de respuesta.

Si decides habilitar el caché distribuido durante la creación de tu proyecto, Redis será integrado a tu contenedor Aspire, proporcionando un sistema de caché de alta disponibilidad.

Ventajas del Caché Distribuido:

  • Mejora del Rendimiento: Reduce el tiempo de respuesta de las solicitudes, minimizando el acceso a la base de datos.
  • Escalabilidad: Permite que la aplicación sea escalada horizontalmente, ya que el caché puede ser accedido por diferentes instancias de forma transparente.
  • Alta Disponibilidad: Con Redis, el caché se mantiene disponible incluso en caso de fallos, ofreciendo una solución robusta para sistemas distribuidos.
  • Reducción de Costos: Disminuye la carga en la base de datos y en el sistema, reduciendo la necesidad de procesamiento en solicitudes repetitivas.

Al habilitar el caché distribuido, mejorarás significativamente el rendimiento de tu aplicación, garantizando una respuesta más rápida a los usuarios y reduciendo la carga en los sistemas backend.

Comunicación Asíncrona

La comunicación asíncrona es un enfoque que permite que los sistemas y componentes se comuniquen de manera no bloqueante, es decir, sin que el envío o recepción de datos interrumpa el procesamiento de otras tareas. Esto es especialmente útil en sistemas distribuidos y en situaciones de alta carga, donde la eficiencia y la resiliencia son cruciales.

Si eliges habilitar la comunicación asíncrona, RabbitMQ será integrado en tu proyecto y MassTransit se configurará para facilitar el uso de esta comunicación asíncrona en tu aplicación.

Ventajas de la Comunicación Asíncrona:

  • Mejor Rendimiento: Permite que las operaciones se realicen en paralelo, sin bloquear el flujo de ejecución del sistema.
  • Escalabilidad: Facilita la escalabilidad del sistema, permitiendo que maneje grandes volúmenes de datos y usuarios simultáneos sin perder rendimiento.
  • Resiliencia: En caso de fallos temporales, la comunicación asíncrona permite que los mensajes sean procesados nuevamente o almacenados para su procesamiento posterior.
  • Desacoplamiento: Los sistemas pueden ser diseñados para comunicarse sin depender directamente de respuestas inmediatas, promoviendo mayor flexibilidad y organización.

La integración con RabbitMQ y el uso de MassTransit hacen que la comunicación entre los componentes sea más eficiente y resiliente, ayudando a garantizar la escalabilidad y flexibilidad de tu aplicación.

Próximos Pasos

Ahora que has creado tu proyecto .NET con Lino, ábrelo en tu editor de código favorito. Puedes usar Visual Studio, Visual Studio Code o cualquier otro IDE de tu elección.

Con el proyecto abierto, puedes comenzar a agregar y configurar los servicios que componen tu aplicación. Este es el siguiente paso en el flujo de desarrollo.

En la siguiente sección, te mostraremos cómo crear y configurar estos servicios en tu nuevo proyecto, preparándolo para crecer de manera organizada y escalable según las necesidades de tu aplicación.

Creación y gestión de servicios

Después de crear el proyecto, el siguiente paso es agregar servicios. Lino ofrece una forma simple e intuitiva de crear servicios, que pueden ser utilizados en sistemas monolíticos o arquitecturas de microservicios.

Para crear un nuevo servicio, utiliza el siguiente comando:

lino service new

Durante la ejecución, el CLI pedirá la siguiente información:

  • Namespace del servicio: Define el nombre y el namespace del servicio.
  • Nombre para mostrar: Nombre amigable mostrado en las interfaces.
  • Tipo de servicio: Elige entre Simple o Modular.
  • Base de datos: Elige entre PostgreSQL o SQL Server.

Si el tipo de servicio elegido es Simple, también se pedirá la siguiente información:

  • Estilo arquitectónico: Actualmente, solo Clean Architecture.
  • ¿Usar Strongly Typed ID? Elige entre Sí o No.

Tipos de servicio

Servicio simple: Un servicio simple tiene una estructura más liviana, adecuada para sistemas monolíticos o proyectos de microservicios donde cada servicio tiene una responsabilidad independiente.

Servicio modular: Para sistemas más grandes, que requieren una mayor organización y escalabilidad, puedes optar por un servicio modular. Este tipo permite dividir el servicio en módulos más pequeños y específicos, facilitando el mantenimiento y la expansión del sistema.

Tanto si el servicio es simple como modular, la base de datos será única para cada servicio. La arquitectura y el uso de Strongly Typed IDs se aplican a los servicios simples. En el caso de los servicios modulares, la decisión sobre esto se tomará a nivel de cada módulo creado dentro del servicio.

La estructura de Lino es flexible, permitiendo crear servicios simples y modulares dentro del mismo proyecto.

Estilo arquitectónico

Lino ya aplica Clean Architecture para todos los servicios. Esto garantiza que tu aplicación siga una arquitectura bien estructurada, promoviendo la separación de responsabilidades y facilitando el mantenimiento y la escalabilidad.

Ventajas de Clean Architecture:

  • Desacoplamiento: La lógica de negocio es independiente de los detalles técnicos, lo que proporciona una mayor flexibilidad y capacidad de prueba.
  • Mantenibilidad: Dado que las capas están bien separadas, es más fácil hacer cambios sin afectar otras partes del sistema.
  • Capacidad de prueba: La separación de preocupaciones facilita la creación de pruebas unitarias e integración para cada capa de forma independiente.
  • Escalabilidad: El proyecto es más fácil de escalar, ya que los componentes pueden ser modificados o reemplazados sin afectar la lógica de negocio.

Si decides crear un servicio simple utilizando la implementación de Clean Architecture, el proyecto será generado con la siguiente estructura:

MyApp/
├── MyApp.sln
├── src/
│   ├── Aspire/
│   │   ├── AppHost/
│   │   │   └── MyApp.AppHost.csproj
│   │   └── ServiceDefaults/
│   │       └── MyApp.ServiceDefaults.csproj
│   ├── Integrations/
│   │   └── Internal/
│   │       └── MySimpleService/
│   │           └── Http/
│   │               ├── Clients/
│   │               │   └── MyApp.Integrations.MySimpleService.Http.Clients.csproj
│   │               └── Contracts/
│   │                   └── MyApp.Integrations.MySimpleService.Http.Contracts.csproj
│   └── Services/
│       ├── Shared/
│       │   ├── API/
│       │   │   └── MyApp.Shared.API.csproj
│       │   ├── Application/
│       │   │   └── MyApp.Shared.Application.csproj
│       │   ├── Domain/
│       │   │   └── MyApp.Shared.Domain.csproj
│       │   ├── Infrastructure/
│       │   │   └── MyApp.Shared.Infrastructure.csproj
│       │   └── Infrastructure.Persistence/
│       │       └── MyApp.Shared.Infrastructure.Persistence.csproj
│       └── MySimpleService/
│           ├── API/
│           │   └── MyApp.MySimpleService.API.csproj
│           ├── Application/
│           │   └── MyApp.MySimpleService.Application.csproj
│           ├── Domain/
│           │   └── MyApp.MySimpleService.Domain.csproj
│           ├── Infrastructure/
│           │   └── MyApp.MySimpleService.Infrastructure.csproj
│           ├── Infrastructure.Persistence/
│           │   └── MyApp.MySimpleService.Infrastructure.Persistence.csproj
│           └── IntegrationEvents/
│               └── MyApp.MySimpleService.IntegrationEvents.csproj
└── tests/
    └── Services/
        └── MySimpleService/
            ├── IntegrationTests/
            │   └── MyApp.MySimpleService.IntegrationTests.csproj
            └── UnitTests/
                └── MyApp.MySimpleService.UnitTests.csproj
    

Si eliges un servicio modular, el proyecto adoptará la siguiente estructura, permitiendo agregar nuevos módulos de forma flexible y organizada a lo largo del desarrollo del servicio:

MyApp/
├── MyApp.sln
├── src/
│   ├── Aspire/
│   │   ├── AppHost/
│   │   │   └── MyApp.AppHost.csproj
│   │   └── ServiceDefaults/
│   │       └── MyApp.ServiceDefaults.csproj
│   └── Services/
│       ├── Shared/
│       │   ├── API/
│       │   │   └── MyApp.Shared.API.csproj
│       │   ├── Application/
│       │   │   └── MyApp.Shared.Application.csproj
│       │   ├── Domain/
│       │   │   └── MyApp.Shared.Domain.csproj
│       │   ├── Infrastructure/
│       │   │   └── MyApp.Shared.Infrastructure.csproj
│       │   └── Infrastructure.Persistence/
│       │       └── MyApp.Shared.Infrastructure.Persistence.csproj
│       └── MyModularService/
│           ├── Host/
│           │   └── MyApp.MyModularService.Host.csproj
│           ├── Infrastructure/
│           │   └── MyApp.MyModularService.Infrastructure.csproj
│           └── Modules/
└── tests/
    

Strongly Typed ID

Strongly Typed ID es un enfoque que busca mejorar la seguridad y la claridad del código al garantizar que los tipos de identificadores (IDs) sean más específicos y fuertemente tipados, evitando el uso de tipos genéricos como int o guid para representar entidades únicas.

Con el Strongly Typed ID, en lugar de usar tipos genéricos, como un número entero para representar un identificador de usuario, crearías un tipo específico para el ID de usuario. Esto ayuda a evitar errores comunes, como el uso incorrecto de IDs de diferentes tipos en contextos equivocados.

Ventajas de usar Strongly Typed IDs:

  • Seguridad de tipos: Garantiza que los IDs se usen correctamente, evitando mezclar diferentes tipos de identificadores, como IDs de usuarios con IDs de productos.
  • Claridad: El código se vuelve más claro, ya que cada tipo de ID se representa explícitamente mediante una clase específica.
  • Facilidad de refactorización: Si el tipo de identificador cambia, solo debes modificar el tipo específico de ID y el resto del código se mantiene seguro.
  • Evita errores: Reduce la posibilidad de errores relacionados con el uso incorrecto de identificadores genéricos en contextos erróneos.

Creación y Gestión de Módulos

Después de crear un servicio modular, el siguiente paso es agregar módulos a él. Los módulos permiten organizar la lógica de negocios de forma independiente, lo que aporta aún más escalabilidad y organización al sistema.

Para crear un nuevo módulo, usa el siguiente comando:

lino module new

Durante la ejecución, la CLI solicitará la siguiente información:

  • Servicio: Define a qué servicio se agregará el nuevo módulo.
  • Namespace del Módulo: Define el nombre y namespace del módulo.
  • Nombre de Visualización: Nombre amigable que se muestra en las interfaces.
  • Estilo Arquitectural: Actualmente solo Clean Architecture.
  • ¿Usar ID fuertemente tipado? Elige entre Sí o No.

Al final, Lino generará el nuevo módulo manteniendo la estructura de tu servicio modular:

MyApp/
├── MyApp.sln
├── src/
│   ├── Aspire/
│   │   ├── AppHost/
│   │   │   └── MyApp.AppHost.csproj
│   │   └── ServiceDefaults/
│   │       └── MyApp.ServiceDefaults.csproj
│   ├── Integrations/
│   │   └── Internal/
│   │       └── MyModularService/
│   │           └── MyModule/
│   │               └── Http/
│   │                   ├── Clients/
│   │                   │   └── MyApp.Integrations.MyModularService.MyModule.Http.Clients.csproj
│   │                   └── Contracts/
│   │                       └── MyApp.Integrations.MyModularService.MyModule.Http.Contracts.csproj
│   └── Services/
│       ├── Shared/
│       │   ├── API/
│       │   │   └── MyApp.Shared.API.csproj
│       │   ├── Application/
│       │   │   └── MyApp.Shared.Application.csproj
│       │   ├── Domain/
│       │   │   └── MyApp.Shared.Domain.csproj
│       │   ├── Infrastructure/
│       │   │   └── MyApp.Shared.Infrastructure.csproj
│       │   └── Infrastructure.Persistence/
│       │       └── MyApp.Shared.Infrastructure.Persistence.csproj
│       └── MyModularService/
│           ├── Host/
│           │   └── MyApp.MyModularService.Host.csproj
│           ├── Infrastructure/
│           │   └── MyApp.MyModularService.Infrastructure.csproj
│           └── Modules/
│               └── MyModule/
│                   ├── API/
│                   │   └── MyApp.MyModularService.MyModule.API.csproj
│                   ├── Application/
│                   │   └── MyApp.MyModularService.MyModule.Application.csproj
│                   ├── Domain/
│                   │   └── MyApp.MyModularService.MyModule.Domain.csproj
│                   ├── Infrastructure/
│                   │   └── MyApp.MyModularService.MyModule.Infrastructure.csproj
│                   ├── Infrastructure.Persistence/
│                   │   └── MyApp.MyModularService.MyModule.Infrastructure.Persistence.csproj
│                   └── IntegrationEvents/
│                       └── MyApp.MyModularService.MyModule.IntegrationEvents.csproj
└── tests/
    └── Services/
        └── MyModularService/
            └── Modules/
                └── MyModule/
                    ├── IntegrationTests/
                    │   └── MyApp.MyModularService.MyModule.IntegrationTests.csproj
                    └── UnitTests/
                        └── MyApp.MyModularService.MyModule.UnitTests.csproj

Estructura de la Base de Datos

Es importante destacar que la base de datos está vinculada al servicio. Dentro de un servicio modular, cada módulo está representado por su propio esquema en la base de datos asociada. Este enfoque proporciona aislamiento y organización, sin necesidad de crear múltiples bases de datos distintas.

Sobre la Independencia entre Módulos

Al igual que los servicios no tienen dependencias directas entre sí, los módulos también se crean como proyectos independientes dentro del servicio.

Ventajas de este desacoplamiento:

  • Aislamiento: Cada módulo puede evolucionar de forma independiente, facilitando el mantenimiento y la evolución continua.
  • Organización: La aplicación se vuelve verdaderamente modular, respetando los límites de contexto (Bounded Contexts) y promoviendo buenas prácticas de arquitectura de software.
  • Flexibilidad: Permite agregar, eliminar o refactorizar módulos sin afectar directamente a otros módulos del servicio.
  • Facilidad para las pruebas: Cada módulo puede probarse de forma aislada, aumentando la confiabilidad y la calidad del sistema.

Con esto, concluimos el proceso de creación de módulos. En los próximos temas, veremos cómo estructurar los elementos internos de un módulo, como entidades, objetos de valor, enumeraciones, comandos, consultas, APIs, integraciones y mucho más.

Se ha producido un error no controlado. Recargar 🗙