使用C#的单一仓库:代码组织的实践
单一仓库(monorepo)是一种源代码管理方法,公司或团队的所有项目都存储在一个仓库中。在C#和.NET的世界里,这种方法越来越受欢迎,因为它简化了协作开发、统一了依赖版本并实现了集中式CI/CD。然而,如果没有正确的代码组织,单一仓库可能会变成一团乱麻。在本文中,我们将探讨在单一仓库中组织C#代码的实用技巧:从文件夹结构到构建配置。
1. 文件夹结构:秩序的基础
成功单一仓库的第一步是精心设计的目录层次结构。建议遵循每个项目放在单独文件夹中的约定,而公共组件则放在src/shared中。基本结构示例:
/monorepo├── src/│ ├── shared/│ │ ├── CommonLib/ # 公共工具、DTO、接口│ │ └── LoggingLib/ # 日志记录│ ├── services/│ │ ├── AuthService/ # 认证微服务│ │ └── OrderService/ # 订单微服务│ └── apps/│ ├── WebApi/ # 主Web应用程序│ └── ConsoleApp/ # 控制台工具├── tests/│ ├── unit/│ │ ├── CommonLib.Tests/│ │ └── AuthService.Tests/│ └── integration/│ └── IntegrationTests/├── docs/├── scripts/├── .gitignore├── Directory.Build.props└── global.json关键点:
- src/ — 所有项目的源代码。
- shared/ — 多个项目使用的库。
- services/ — 微服务或模块。
- apps/ — 最终应用程序(Web API、Console、Blazor)。
- tests/ — 测试,与源代码隔离。
这种结构便于查找代码、避免循环依赖并简化CI/CD配置。
2. 使用Directory.Build.props管理项目
在单一仓库中,经常需要为所有项目设置公共配置:.NET版本、公共包、代码分析规则。与其在每个.csproj中重复,不如在仓库根目录使用Directory.Build.props文件。示例:
<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>现在每个子项目都会自动继承这些设置。如果需要为特定项目覆盖设置,只需在其.csproj中添加属性即可——它们具有更高优先级。
此外,可以创建Directory.Build.targets用于公共构建目标(例如,构建后复制文件)。
3. 依赖管理:集中式方法
单一仓库的主要问题之一是包版本冲突。在.NET 8中,内置支持集中式包管理(Central Package Management, CPM)。为此,在根目录创建Directory.Packages.props文件:
<Project> <PropertyGroup> <ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally> </PropertyGroup> <ItemGroup> <PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" /> <PackageVersion Include="Serilog" Version="3.1.1" />