Estructurando el Proyecto

Lino estructura soluciones .NET para que las decisiones arquitectónicas queden explícitas desde el primer commit. Un proyecto generado ya nace con orquestación Aspire, bloques compartidos, pruebas, configuración de calidad de código y un lugar claro para cada servicio, módulo, contrato de API, preocupación de persistencia y límite de integración.


Esta sección explica cómo lino project new, lino service new y lino module new dan forma a la solución. El objetivo no es solo crear carpetas, sino definir límites de runtime, ownership de base de datos, aislamiento de módulos y un camino de evolución desde un servicio simple hacia un monolito modular o un sistema distribuido.

Creación de la Fundación de la Solución

El comando lino project new crea la fundación técnica de una nueva solución .NET. Ejecútalo en un directorio vacío después de instalar y autenticar la CLI.

lino project new --name <ProjectName>

El argumento <ProjectName> representa el nombre real de la solución. Ese nombre pasa a formar parte de namespaces, assemblies, rutas, configuraciones, artefactos y referencias entre componentes; por eso, elige un nombre corto, estable y representativo.

El asistente interactivo solicita decisiones que afectan la estructura y el comportamiento de runtime de toda la solución:

  • Namespace del proyecto: identidad técnica raíz usada por los proyectos generados.
  • Nombre de visualización: nombre amigable utilizado en metadatos generados y puntos visibles para el usuario.
  • Lenguaje y stack: actualmente C# con .NET 10 y Aspire.
  • Analizadores de código: habilitan paquetes y reglas compartidas para mantener consistencia y calidad desde el bootstrap.
  • CQRS: prepara la capa de aplicación para separar commands y queries, con orquestación por la biblioteca de mediator seleccionada.
  • Clases base en la solución: controla si se generarán abstracciones comunes localmente dentro de la solución.
  • Cache distribuido: define si Microsoft.Extensions.Caching.Hybrid usará solo memoria local de la instancia o también una capa distribuida con Redis configurada por Aspire.
  • Comunicación asíncrona: habilita RabbitMQ con MassTransit y los bloques de mensajería/outbox usados por eventos de integración.
  • Idioma de los datos: idioma usado para describir metadatos de dominio durante la generación.
  • Culturas soportadas por la aplicación: recursos de localización generados para textos de UI, validaciones, errores y respuestas de API.
  • Cultura predeterminada: idioma principal usado cuando la aplicación necesita un fallback.

Después de la confirmación, Lino genera una solución organizada para crecer. Un proyecto mínimo nace con Aspire, capas compartidas y pruebas del área Shared:

<ProjectName>/
├── <ProjectName>.slnx
├── Directory.Build.props
├── Directory.Packages.props
├── src/
│   ├── Aspire/
│   │   ├── AppHost/
│   │   │   └── <ProjectName>.AppHost.csproj
│   │   └── ServiceDefaults/
│   │       └── <ProjectName>.ServiceDefaults.csproj
│   └── Services/
│       └── Shared/
│           ├── Api/
│           │   └── <ProjectName>.Shared.Api.csproj
│           ├── Application/
│           │   └── <ProjectName>.Shared.Application.csproj
│           ├── Domain/
│           │   └── <ProjectName>.Shared.Domain.csproj
│           ├── Infrastructure/
│           │   └── <ProjectName>.Shared.Infrastructure.csproj
│           ├── Infrastructure.Persistence/
│           │   └── <ProjectName>.Shared.Infrastructure.Persistence.csproj
│           └── Integration.Events/          (cuando haya mensajería)
│               └── <ProjectName>.Shared.Integration.Events.csproj
└── tests/
    └── Services/
        └── Shared/
            └── UnitTests/
                ├── Domain/
                │   └── <ProjectName>.Shared.Domain.UnitTests.csproj
                └── Application/
                    └── <ProjectName>.Shared.Application.UnitTests.csproj

El papel de los proyectos Shared

Shared contiene código de plataforma compartido por la solución. No es un servicio de negocio. Usa este espacio para abstracciones transversales, errores comunes, infraestructura de localización, contratos base de aplicación, helpers de persistencia, extensiones de API, integración con host, observabilidad y utilidades técnicas reutilizables.

Evita colocar reglas de negocio en Shared. Si una regla pertenece a una capacidad específica de la aplicación, debe quedar en el servicio o módulo que tiene esa responsabilidad. El objetivo de Shared es reducir repetición técnica, no convertirse en un atajo para acoplar dominios diferentes.

Aspire y decisiones de infraestructura

AppHost compone la solución y sus recursos de runtime. Cuando se agregan Redis, RabbitMQ, SQL Server, PostgreSQL, Redis Insight, servicios, WebApps o workers, Aspire se convierte en el punto de orquestación local. Esto simplifica ejecución, descubrimiento de servicios, logs, métricas, traces y visualización de recursos durante el desarrollo.

ServiceDefaults centraliza patrones de hosting como service discovery, health checks, resiliencia, logging, métricas, tracing e integración con OpenTelemetry. En lugar de que cada servicio configure esto de forma aislada, la solución nace con un punto común de composición.

Analizadores de código

Los analizadores de código estático inspeccionan el código durante el desarrollo y hacen visibles los problemas antes de la ejecución: inconsistencias de estilo, patrones frágiles, oportunidades de refactorización, posibles bugs y alertas de seguridad.

Cuando habilitas analizadores en lino project new, la solución ya nace con paquetes como StyleCop.Analyzers, SonarAnalyzer.CSharp y Roslynator.Analyzers configurados de forma centralizada. Esto evita que cada proyecto necesite decidir por separado qué reglas seguir.

  • Mejora de calidad: mantiene el código legible, consistente y alineado con los estándares de la solución.
  • Prevención de errores: señala problemas temprano, antes de que lleguen a pruebas manuales o producción.
  • Estandarización: reduce diferencias de estilo entre servicios, módulos y equipos.
  • Refactorización asistida: destaca simplificaciones y modernizaciones posibles en el código C#.

Cache distribuido, cache híbrido y Redis

Lino prepara la solución para usar Microsoft.Extensions.Caching.Hybrid, la biblioteca de Microsoft que centraliza operaciones de cache a través de HybridCache. Esta abstracción permite que handlers, servicios de aplicación y componentes de infraestructura almacenen resultados de consultas, permisos, configuraciones o datos de apoyo sin dispersar detalles de implementación de cache por el código.

Cuando no habilitas cache distribuido en la creación del proyecto, HybridCache sigue disponible, pero trabaja restringido a la memoria local de la instancia en ejecución. Este modo es simple y suficiente para escenarios locales, ambientes pequeños o aplicaciones con una sola réplica, pero cada proceso mantiene su propio cache y los datos no se comparten entre instancias.

Cuando habilitas cache distribuido, Lino agrega Redis a los recursos de Aspire y configura la infraestructura para usar una capa de cache compartida. Con Redis, múltiples instancias del mismo servicio pueden consultar la misma capa, reduciendo lecturas repetidas en bases de datos, APIs internas o integraciones y preparando el proyecto para escala horizontal.

  • Performance: reduce el tiempo de respuesta en lecturas repetidas y operaciones de apoyo.
  • Escalabilidad: permite que instancias diferentes compartan datos en cache.
  • Disponibilidad: desacopla parte de la carga de lectura de la base de datos principal.
  • Costo operacional: disminuye procesamiento repetitivo en consultas e integraciones de alto volumen.

Esta decisión se toma en lino project new porque altera la fundación del entorno: recursos de AppHost, secrets locales, paquetes, configuración de infraestructura y la topología de ejecución mostrada en el dashboard de Aspire.

Comunicación asíncrona

La comunicación asíncrona permite que servicios, módulos y componentes reaccionen a hechos del sistema sin bloquear el flujo principal de la operación. Es especialmente útil cuando el productor no debe depender de la disponibilidad inmediata del consumidor o cuando una acción necesita disparar efectos posteriores, como notificaciones, proyecciones, integraciones externas o sincronización entre contextos.

Cuando habilitas comunicación asíncrona, Lino agrega RabbitMQ a Aspire y configura MassTransit junto con los bloques de mensajería y outbox usados por eventos de integración. Así, los eventos publicados por servicios o módulos quedan representados por contratos explícitos en Integration.Events y pueden procesarse con más resiliencia.

  • Desempeño: permite continuar el caso de uso principal sin esperar que todos los consumidores terminen.
  • Escalabilidad: distribuye procesamiento por consumidores y colas, absorbiendo picos con más control.
  • Resiliencia: permite reprocesamiento y reduce pérdida de mensajes cuando se combina con outbox.
  • Desacoplamiento: evita dependencia directa entre productor y consumidor cuando la respuesta inmediata no es necesaria.

Usa eventos de integración cuando una falla del consumidor no debe deshacer la transacción del productor. Cuando el consumidor necesita responder inmediatamente para completar el caso de uso, prefiere una integración síncrona explícita y trata disponibilidad, timeout y fallback como parte del contrato.

Localización y culturas

Cuando se seleccionan culturas, Lino genera recursos para que mensajes, validaciones, labels, errores y textos de UI puedan localizarse desde el inicio. Esto evita tratar la internacionalización como un parche tardío, cuando las strings ya están dispersas por endpoints, handlers y componentes.

Valida la fundación y avanza hacia servicios

Después de la generación, restaura dependencias, compila la solución y ejecuta el host de Aspire:

dotnet restore <ProjectName>.slnx
dotnet build <ProjectName>.slnx
dotnet run --project src/Aspire/AppHost/<ProjectName>.AppHost.csproj

En esta etapa, la solución todavía es una fundación. Abre el proyecto en el editor, revisa los recursos creados por Aspire, confirma las decisiones de cache, mensajería, localización y calidad de código, y luego avanza hacia los servicios que representarán las capacidades de negocio de la aplicación.

Creación y Gestión de Servicios

Un servicio es un límite de runtime y ownership. En Lino, los servicios pueden representar APIs independientes en un sistema distribuido o áreas mayores de negocio dentro de una solución que puede evolucionar gradualmente.

Después de crear la fundación del proyecto, agrega servicios con:

lino service new

El asistente de servicio solicita:

  • Namespace del servicio: nombre técnico usado en carpetas, proyectos y namespaces.
  • Nombre de visualización: nombre amigable del servicio.
  • Tipo de servicio: elige entre simple y modular.
  • Proveedor de base de datos: elige SQL Server o PostgreSQL para la base de datos del servicio.
  • Estilo de arquitectura: actualmente Clean Architecture para servicios simples.
  • Strongly Typed IDs: en servicios simples, define si los identificadores se generarán como tipos dedicados.

Tipos de servicio

Servicio simple: estructura más directa para una capacidad de negocio con frontera clara. Es una buena elección para una API enfocada, un microservicio o un área lo suficientemente pequeña como para evolucionar como una unidad.

Servicio modular: estructura indicada para sistemas mayores, monolitos modulares o runtimes que necesitan hospedar varias capacidades independientes. Los módulos aumentan la organización y la escalabilidad del modelo, pero exigen más disciplina de dependencia.

Independientemente del tipo, cada servicio es dueño de su propia base de datos. En servicios simples, la decisión sobre Strongly Typed IDs se toma en el servicio; en servicios modulares, se toma por módulo.

Servicio simple

Un servicio simple coloca sus capas directamente en src/Services/<ServiceName>. Usa esta estructura cuando la capacidad de negocio tiene un límite claro y no necesita varios módulos aislados dentro del mismo runtime. Funciona bien para una API enfocada, un microservicio o un área de dominio lo suficientemente pequeña como para evolucionar como una unidad.

src/Services/<ServiceName>/
├── Domain/
│   └── <ProjectName>.<ServiceName>.Domain.csproj
├── Application/
│   └── <ProjectName>.<ServiceName>.Application.csproj
├── Infrastructure.Persistence/
│   └── <ProjectName>.<ServiceName>.Infrastructure.Persistence.csproj
├── Infrastructure/
│   └── <ProjectName>.<ServiceName>.Infrastructure.csproj
├── Api/
│   └── <ProjectName>.<ServiceName>.Api.csproj
├── Integration.Events/              (cuando haya mensajería)
│   └── <ProjectName>.<ServiceName>.Integration.Events.csproj
├── Api.Contracts/                   (cuando haya consumo HTTP tipado)
│   └── <ProjectName>.<ServiceName>.Api.Contracts.csproj
└── Api.Client/                      (cuando haya consumo HTTP tipado)
    └── <ProjectName>.<ServiceName>.Api.Client.csproj
tests/Services/<ServiceName>/
├── UnitTests/
│   ├── Domain/
│   │   └── <ProjectName>.<ServiceName>.Domain.UnitTests.csproj
│   └── Application/
│       └── <ProjectName>.<ServiceName>.Application.UnitTests.csproj
└── IntegrationTests/
    └── <ProjectName>.<ServiceName>.IntegrationTests.csproj

Las capas tienen responsabilidades distintas: Domain protege reglas de negocio e invariantes; Application orquesta casos de uso; Infrastructure.Persistence contiene Entity Framework Core, repositorios, Unit of Work y migrations; Infrastructure contiene integraciones técnicas; y Api adapta HTTP para casos de uso.

Los proyectos Api.Contracts y Api.Client aparecen cuando hay consumo HTTP tipado, especialmente en soluciones con Web App Blazor. El primero concentra requests, responses, DTOs, tipos públicos e interfaces de client compartidas; el segundo proporciona la implementación HTTP de esas interfaces, usando HttpClient, para que Blazor consuma las APIs generadas sin duplicar contratos.

El proyecto Integration.Events aparece cuando la solución tiene mensajería habilitada. Contiene eventos de integración publicados y consumidos por otros módulos, servicios o sistemas, normalmente junto con la infraestructura de mensajería y outbox.

Servicio modular

Un servicio modular está indicado para monolitos modulares o para un runtime que hospeda múltiples bounded contexts. El servicio en sí tiene el host, infraestructura común y proveedor de base de datos; las reglas de negocio viven dentro de los módulos.

src/Services/<ServiceName>/
├── Host/
│   └── <ProjectName>.<ServiceName>.Host.csproj
├── Infrastructure/
│   └── <ProjectName>.<ServiceName>.Infrastructure.csproj
└── Modules/
tests/Services/<ServiceName>/
└── Modules/

El Host compone módulos, configuraciones, endpoints e infraestructura compartida de ese servicio. No debe contener reglas de negocio. El proyecto Infrastructure en el nivel del servicio proporciona soporte técnico de composición compartido por los módulos.

Cuando se agrega un módulo, los proyectos de Domain, Application, Infrastructure.Persistence, Infrastructure y Api aparecen dentro de Modules/<ModuleName>, preservando la frontera interna. Los proyectos Api.Contracts, Api.Client e Integration.Events siguen las mismas condiciones: consumo HTTP tipado para Blazor/API clients y mensajería habilitada para eventos de integración.

Ownership de base de datos

Cada servicio es dueño de su propia base de datos. En una solución con múltiples servicios, un servicio puede usar PostgreSQL mientras otro usa SQL Server. Esta decisión puede variar por necesidad de dominio, performance, operación, madurez del equipo o integración con infraestructura existente.

En un servicio modular, la base pertenece al servicio como runtime, pero los módulos quedan aislados por schema, proyectos de persistencia y migrations propias. Un módulo no debe consultar directamente tablas de otro módulo, incluso cuando las tablas están en la misma base física.

Elección entre simple y modular

Elige la menor estructura que proteja el límite. Un servicio simple es suficiente cuando existe una única frontera de dominio. Un servicio modular tiene sentido cuando varias capacidades necesitan compartir runtime, deploy o transacción, pero todavía deben mantener modelos, persistencia, APIs y pruebas separados.

ElecciónCuándo usarCosto asumido
Servicio simpleUn dominio enfocado, pocas fronteras internas, API aislada o microservicio directo.Menos estructura inicial, pero menos aislamiento interno si el dominio crece demasiado.
Servicio modularMúltiples subdominios en el mismo runtime, monolito modular, SaaS con áreas independientes o necesidad de schemas por módulo.Más proyectos, más disciplina de dependencia y mayor atención a contratos internos.

Estilo de arquitectura

Los servicios generados por Lino siguen Clean Architecture para separar reglas de negocio de detalles técnicos. La capa de dominio no necesita conocer HTTP, Entity Framework Core, mensajería, UI o proveedores externos; estas preocupaciones quedan en los bordes de la aplicación.

  • Desacoplamiento: las reglas centrales no dependen de frameworks o mecanismos de entrega.
  • Mantenibilidad: los cambios en infraestructura tienden a quedar aislados de las reglas de negocio.
  • Testabilidad: los casos de uso y el dominio pueden probarse con menos dependencias externas.
  • Evolución: los detalles técnicos pueden sustituirse con menor impacto en el núcleo del servicio.

Clean Architecture y Strongly Typed IDs

Los servicios generados siguen Clean Architecture para que el código de negocio no dependa de HTTP, EF Core, mensajería, UI o detalles de infraestructura. Esta separación hace que las pruebas sean más directas, reduce el acoplamiento y permite sustituir detalles técnicos sin reescribir reglas centrales.

Strongly Typed IDs aumentan seguridad al impedir mezclas accidentales de identificadores. En lugar de aceptar cualquier Guid, long o int, el dominio puede trabajar con un tipo propio para cada entidad o agregado, normalmente en el formato <EntityName>Id. Esto evita pasar el identificador de una entidad donde se esperaba otra y deja las firmas más expresivas en commands, queries, entidades, handlers y mapeos.

  • Seguridad de tipo: impide mezclar identificadores de entidades diferentes por accidente.
  • Claridad: deja firmas y contratos más expresivos que tipos primitivos sueltos.
  • Refactorización: concentra cambios de formato o serialización en el tipo de ID correspondiente.
  • Reducción de errores: vuelve visibles los usos incorrectos en compilación siempre que sea posible.

Integración entre servicios

Cuando un servicio necesita reaccionar a otro, prefiere eventos de integración, integraciones HTTP explícitas o datos replicados conscientemente. El acceso directo a la base de datos de otro servicio crea acoplamiento estructural, dificulta migrations y vuelve más riesgosos los deploys independientes.

Próximos pasos para servicios modulares

Después de crear un servicio modular, el próximo paso es agregar módulos con lino module new. Cada módulo debe representar una capacidad de negocio propia, con dominio, aplicación, persistencia, API, pruebas e integraciones preservando el límite interno del servicio.

Creación y Gestión de Módulos

Los módulos existen solo dentro de servicios modulares. Un módulo representa un límite de negocio dentro del mismo runtime: tiene su propio modelo de dominio, casos de uso, persistencia, superficie de API, eventos de integración, pruebas y schema de base de datos.

Usa módulos cuando el servicio necesita hospedar más de una capacidad de negocio sin mezclar entidades, reglas, migrations y contratos. En un monolito modular, todo puede ejecutarse en el mismo proceso, pero los límites internos siguen siendo importantes: el aislamiento real es control de dependencias, no solo separación de carpetas.

lino module new --service <ServiceName>

Durante la creación, el asistente solicita:

  • Servicio: el servicio modular que hospedará el módulo. Lino no permite crear módulos dentro de servicios simples, porque no tienen la estructura necesaria para el aislamiento interno.
  • Namespace del módulo: el nombre técnico usado en carpetas, namespaces, assemblies y proyectos.
  • Nombre de visualización: el nombre amigable del módulo en lugares orientados a lectura humana.
  • Strongly Typed IDs: define si los identificadores generados dentro del módulo usarán tipos dedicados, normalmente en el formato <EntityName>Id.

Después de la confirmación, Lino agrega el módulo dentro del servicio sin colapsar el límite arquitectural:

src/Services/<ServiceName>/
├── Host/
├── Infrastructure/
└── Modules/
    └── <ModuleName>/
        ├── Domain/
        │   └── <ProjectName>.<ServiceName>.<ModuleName>.Domain.csproj
        ├── Application/
        │   └── <ProjectName>.<ServiceName>.<ModuleName>.Application.csproj
        ├── Infrastructure.Persistence/
        │   └── <ProjectName>.<ServiceName>.<ModuleName>.Infrastructure.Persistence.csproj
        ├── Infrastructure/
        │   └── <ProjectName>.<ServiceName>.<ModuleName>.Infrastructure.csproj
        ├── Api/
        │   └── <ProjectName>.<ServiceName>.<ModuleName>.Api.csproj
        ├── Integration.Events/              (cuando haya mensajería)
        │   └── <ProjectName>.<ServiceName>.<ModuleName>.Integration.Events.csproj
        ├── Api.Contracts/                   (cuando haya consumo HTTP tipado)
        │   └── <ProjectName>.<ServiceName>.<ModuleName>.Api.Contracts.csproj
        └── Api.Client/                      (cuando haya consumo HTTP tipado)
            └── <ProjectName>.<ServiceName>.<ModuleName>.Api.Client.csproj
tests/Services/<ServiceName>/Modules/<ModuleName>/
├── UnitTests/
│   ├── Domain/
│   │   └── <ProjectName>.<ServiceName>.<ModuleName>.Domain.UnitTests.csproj
│   └── Application/
│       └── <ProjectName>.<ServiceName>.<ModuleName>.Application.UnitTests.csproj
└── IntegrationTests/
    └── <ProjectName>.<ServiceName>.<ModuleName>.IntegrationTests.csproj

Responsabilidades de los artefactos generados

ArtefactoResponsabilidad dentro del módulo
DomainEntidades, agregados, Value Objects, enumerations, eventos de dominio, contratos de repositorio e invariantes del módulo.
ApplicationCasos de uso, commands, queries, handlers, validaciones, contratos internos de entrada y salida, y orquestación de las reglas del módulo.
Infrastructure.PersistenceDbContext, configuraciones de Entity Framework Core, repositorios concretos, Unit of Work y migrations del módulo.
InfrastructureImplementaciones técnicas específicas del módulo, adaptadores, providers y composición de dependencias.
ApiEndpoints HTTP, versionamiento, filtros, autorización y adaptación entre solicitudes externas y casos de uso.
Api.ContractsGenerado cuando hay consumo HTTP tipado, normalmente por Web Apps Blazor. Contiene requests, responses, DTOs, tipos públicos e interfaces de client compartidas entre la API y el consumidor.
Api.ClientGenerado junto con los contratos cuando hay consumo HTTP tipado. Contiene la implementación HTTP de las interfaces, usando HttpClient, providers, options y helpers para que los proyectos Blazor consuman las APIs generadas de forma consistente y fuertemente tipada.
Integration.EventsGenerado cuando el proyecto tiene mensajería. Contiene eventos de integración disparados y consumidos por otros módulos, servicios o sistemas, manteniendo el payload como contrato explícito.

Estructura de base de datos

La base de datos sigue vinculada al servicio, no al módulo de forma aislada. Dentro de un servicio modular, cada módulo está representado por su propio schema en la base asociada, con proyecto de persistencia y migrations propias. Esto ofrece aislamiento y organización sin exigir múltiples bases físicas para cada módulo.

Aislamiento e independencia entre módulos

Un módulo no debe acceder directamente al DbContext, las entidades, los repositorios o los servicios internos de otro módulo. Cada módulo tiene su propio modelo y su propia persistencia. Cuando otro módulo necesita datos, usa una integración explícita en lugar de atravesar la frontera mediante detalles internos de implementación.

Esto evita que un cambio aparentemente local rompa otra área del sistema. Si un módulo consumidor necesita consultar datos que pertenecen a otro módulo, no debe depender de la entidad completa del módulo de origen. Puede mantener una shadow entity con los datos mínimos necesarios para su propio caso de uso, alimentada por integración o evento.

Ventajas de este desacoplamiento:

  • Aislamiento: cada módulo puede evolucionar reglas, persistencia y pruebas sin atravesar detalles internos de otro módulo.
  • Organización: la aplicación respeta bounded contexts y deja ownership explícito.
  • Flexibilidad: los módulos se pueden agregar, eliminar o refactorizar con menor impacto en el resto del servicio.
  • Facilidad de pruebas: cada módulo puede validarse de forma más independiente, aumentando la confianza en los cambios.

Comunicación entre módulos

Usa contratos explícitos para la comunicación. Para llamadas dentro del mismo runtime, una integración interna puede exponer contratos en Integration.Contracts e implementación in-process cuando se genera. Para llamadas entre runtimes, usa integración HTTP. Para propagación asíncrona, publica eventos de integración en Integration.Events y usa la infraestructura de mensajería/outbox cuando la publicación forme parte de una operación transaccional.

Schemas, migrations y base de datos

En un servicio modular, los módulos comparten el proveedor de base de datos del servicio, pero cada módulo está representado por su propio schema y por su propio proyecto Infrastructure.Persistence. Las migrations se generan por módulo, manteniendo la evolución de la base alineada con el límite de negocio.

Esta separación permite que cada módulo evolucione sus tablas, seeds, índices y foreign keys sin transformar la base del servicio en un modelo único compartido por todos. El schema es un límite técnico que refuerza el límite de negocio, incluso cuando los módulos se ejecutan en el mismo proceso y usan la misma base física.

Buenos límites de módulo

Crea módulos alrededor de capacidades de negocio, no alrededor de capas técnicas. Un buen nombre de módulo debe describir una responsabilidad reconocible por el dominio, como un área, proceso o capacidad que tiene reglas propias. Evita nombres genéricos como Common, Core o Utilities para reglas de negocio, porque ocultan ownership y tienden a convertirse en dependencias demasiado compartidas.

  • Cohesión: el módulo debe tener lenguaje, reglas y datos que cambian juntos.
  • Autonomía: debe ser posible probar y evolucionar el módulo sin acceder a entidades internas de otro módulo.
  • Contratos claros: los datos necesarios fuera del módulo deben exponerse mediante API, integración, evento o shadow entity, no por acceso directo a la base.
  • Bajo acoplamiento: si dos módulos necesitan cambiar las mismas entidades todo el tiempo, probablemente el límite necesita revisarse.

Con los módulos creados, los siguientes temas de la documentación muestran cómo modelar entidades, Value Objects, enumerations, commands, queries, APIs, eventos, integraciones y migrations dentro de estos límites.

Se ha producido un error no controlado. Recargar 🗙