Monorepositorio con C#: organización del código en la práctica
Un monorepositorio (monorepo) es un enfoque para gestionar el código fuente donde todos los proyectos de una empresa o equipo se almacenan en un único repositorio. En el mundo de C# y .NET, este enfoque se vuelve cada vez más popular debido a la simplificación del desarrollo colaborativo, la unificación de versiones de dependencias y el CI/CD centralizado. Sin embargo, sin una organización adecuada del código, un monorepositorio puede convertirse en un caos. En este artículo, analizaremos técnicas prácticas para organizar código C# en un monorepositorio: desde la estructura de carpetas hasta la configuración de la compilación.
1. Estructura de carpetas: la base del orden
El primer paso hacia un monorepositorio exitoso es una jerarquía de directorios bien pensada. Se recomienda seguir una convención donde cada proyecto se encuentra en una carpeta separada y los componentes comunes se colocan en src/shared. Ejemplo de estructura básica:
/monorepo├── src/│ ├── shared/│ │ ├── CommonLib/ # Utilidades comunes, DTO, interfaces│ │ └── LoggingLib/ # Registro de logs│ ├── services/│ │ ├── AuthService/ # Microservicio de autenticación│ │ └── OrderService/ # Microservicio de pedidos│ └── apps/│ ├── WebApi/ # Aplicación web principal│ └── ConsoleApp/ # Herramienta de consola├── tests/│ ├── unit/│ │ ├── CommonLib.Tests/│ │ └── AuthService.Tests/│ └── integration/│ └── IntegrationTests/├── docs/├── scripts/├── .gitignore├── Directory.Build.props└── global.jsonPuntos clave:
- src/ — código fuente de todos los proyectos.
- shared/ — bibliotecas utilizadas por varios proyectos.
- services/ — microservicios o módulos.
- apps/ — aplicaciones finales (Web API, Consola, Blazor).
- tests/ — pruebas, aisladas del código fuente.
Esta estructura facilita la búsqueda de código, evita dependencias cíclicas y simplifica la configuración de CI/CD.
2. Gestión de proyectos con Directory.Build.props
En un monorepositorio, a menudo es necesario establecer configuraciones comunes para todos los proyectos: versiones de .NET, paquetes compartidos, reglas de análisis de código. En lugar de duplicarlas en cada .csproj, use el archivo Directory.Build.props en la raíz del repositorio. Ejemplo:
<Project> <PropertyGroup> <TargetFramework>net8.0</TargetFramework> <ImplicitUsings>enable</ImplicitUsings> <Nullable>enable</Nullable> <TreatWarningsAsErrors>true</TreatWarningsAsErrors> </PropertyGroup>
<ItemGroup> <PackageReference Include="SonarAnalyzer.CSharp" Version="9.*" /> </ItemGroup></Project>Ahora cada proyecto hijo hereda automáticamente estas configuraciones. Si necesita anularlas para un proyecto específico, simplemente agregue las propiedades en su .csproj; estas tienen prioridad.
Adicionalmente, puede crear Directory.Build.targets para objetivos de compilación comunes (por ejemplo, copiar archivos después de la compilación).
3. Gestión de dependencias: enfoque centralizado
Uno de los principales problemas de un monorepositorio es el conflicto de versiones de paquetes. En .NET 8 se introdujo soporte integrado para la gestión centralizada de paquetes (Central Package Management, CPM). Para ello, cree el archivo Directory.Packages.props en la raíz:
<Project> <PropertyGroup> <ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally> </PropertyGroup> <ItemGroup> <PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" /> <PackageVersion Include="Serilog" Version="3.1.1" /&g