Monorepositório com C#: organização de código na prática
Monorepositório (monorepo) é uma abordagem para gerenciamento de código-fonte onde todos os projetos de uma empresa ou equipe são armazenados em um único repositório. No mundo C# e .NET, essa abordagem está se tornando cada vez mais popular devido à simplificação do desenvolvimento colaborativo, unificação de versões de dependências e CI/CD centralizado. No entanto, sem uma organização adequada do código, um monorepositório pode se transformar em caos. Neste artigo, vamos explorar técnicas práticas para organizar código C# em um monorepositório: desde a estrutura de pastas até a configuração de build.
1. Estrutura de pastas: a base da ordem
O primeiro passo para um monorepositório bem-sucedido é uma hierarquia de diretórios bem planejada. Recomenda-se seguir uma convenção onde cada projeto fica em uma pasta separada, e os componentes comuns são colocados em src/shared. Exemplo de estrutura básica:
/monorepo├── src/│ ├── shared/│ │ ├── CommonLib/ # Utilitários comuns, DTOs, interfaces│ │ └── LoggingLib/ # Logging│ ├── services/│ │ ├── AuthService/ # Microsserviço de autenticação│ │ └── OrderService/ # Microsserviço de pedidos│ └── apps/│ ├── WebApi/ # Aplicação web principal│ └── ConsoleApp/ # Ferramenta de console├── tests/│ ├── unit/│ │ ├── CommonLib.Tests/│ │ └── AuthService.Tests/│ └── integration/│ └── IntegrationTests/├── docs/├── scripts/├── .gitignore├── Directory.Build.props└── global.jsonPontos-chave:
- src/ — código-fonte de todos os projetos.
- shared/ — bibliotecas usadas por vários projetos.
- services/ — microsserviços ou módulos.
- apps/ — aplicações finais (Web API, Console, Blazor).
- tests/ — testes, isolados do código-fonte.
Essa estrutura facilita encontrar código, evitar dependências cíclicas e simplifica a configuração de CI/CD.
2. Gerenciamento de projetos com Directory.Build.props
Em um monorepositório, frequentemente surge a necessidade de definir configurações comuns para todos os projetos: versões do .NET, pacotes compartilhados, regras de análise de código. Em vez de duplicar em cada .csproj, use o arquivo Directory.Build.props na raiz do repositório. Exemplo:
<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>Agora, cada projeto filho herda automaticamente essas configurações. Se precisar sobrescrever para um projeto específico, basta adicionar as propriedades no .csproj dele — elas têm prioridade.
Adicionalmente, você pode criar um Directory.Build.targets para objetivos de build comuns (por exemplo, copiar arquivos após o build).
3. Gerenciamento de dependências: abordagem centralizada
Um dos principais problemas de um monorepositório é o conflito de versões de pacotes. No .NET 8, foi introduzido suporte nativo para gerenciamento centralizado de pacotes (Central Package Management, CPM). Para isso, crie o arquivo Directory.Packages.props na raiz:
<Project> <PropertyGroup> <ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally> </PropertyGroup> <ItemGroup> <PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" /> <PackageVersion Include="Serilog" Version="3.1.1" /&g