Монорепозиторий с C#: организация кода на практике
Монорепозиторий (monorepo) — это подход к управлению исходным кодом, при котором все проекты компании или команды хранятся в одном репозитории. В мире C# и .NET этот подход становится всё популярнее благодаря упрощению совместной разработки, унификации версий зависимостей и централизованному CI/CD. Однако без правильной организации кода монорепозиторий может превратиться в хаос. В этой статье мы разберём практические приёмы организации C#-кода в монорепозитории: от структуры папок до настройки сборки.
1. Структура папок: основа порядка
Первый шаг к успешному монорепозиторию — продуманная иерархия каталогов. Рекомендуется придерживаться конвенции, где каждый проект лежит в отдельной папке, а общие компоненты вынесены в src/shared. Пример базовой структуры:
/monorepo
├── src/
│ ├── shared/
│ │ ├── CommonLib/ # Общие утилиты, DTO, интерфейсы
│ │ └── LoggingLib/ # Логирование
│ ├── services/
│ │ ├── AuthService/ # Микросервис аутентификации
│ │ └── OrderService/ # Микросервис заказов
│ └── apps/
│ ├── WebApi/ # Основное веб-приложение
│ └── 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" />
<PackageVersion Include="xunit" Version="2.7.0" />
</ItemGroup>
</Project>
Теперь в каждом .csproj достаточно указать только имя пакета без версии:
<ItemGroup>
<PackageReference Include="Serilog" />
<PackageReference Include="xunit" />
</ItemGroup>
Версии будут подтянуты из центрального файла. Это гарантирует, что все проекты используют одинаковые версии зависимостей, и упрощает обновление.
4. Сборка и CI/CD: автоматизация без боли
Для сборки монорепозитория удобно использовать solution-файлы (.sln). Создайте один или несколько solution-файлов, группируя проекты по логике (например, Monorepo.sln для всех проектов, Services.sln только для микросервисов).
Пример команды для восстановления и сборки всех проектов:
dotnet restore Monorepo.sln
dotnet build Monorepo.sln --configuration Release
В CI/CD (например, GitHub Actions) можно настроить триггеры на изменения в конкретных папках, чтобы не пересобирать весь репозиторий. Пример конфигурации для YAML:
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: '8.0.x'
- name: Restore
run: dotnet restore Monorepo.sln
- name: Build
run: dotnet build Monorepo.sln --configuration Release --no-restore
- name: Test
run: dotnet test Monorepo.sln --configuration Release --no-build
Для оптимизации можно добавить кэширование ~/.nuget/packages и использовать
Блоги
Рекомендаций по книгам
Читайте также
Лучшие IDE и инструменты для Swift разработки: продвинутый уровень
Кэширование в PHP: Redis, Memcached и встроенные решения для ускорения сайта
Чистый код на Swift: как писать так, чтобы коллеги не проклинали вас
Тестирование TypeScript: пошаговое руководство для новичков
Регулярные выражения в PHP: полное руководство с примерами